TODO tratar de NAs e passar tabelas pra csv

How to give name to media

For R beginners

New chunk Ctrl+Alt+I

Execute chunk Ctrl+Shift+Enter

Execute all chunks Ctrl+Alt+R

HTML preview Ctrl+Shift+K

Library preparations

library(readr)
library(dplyr)
library(tidyverse)
library(ggplot2)
library(reshape2)
library(stats)

Data Import

data <- read.csv("~/4year/2semester/dtII/CSVs/HEIs.csv",
                 colClasses = c(tweet_id = "character"))

# Modifying created_at type so that attribute can be used more easily 
data$created_at <- as.POSIXct(data$created_at,
                              format= "%Y-%m-%dT%H:%M:%S", tz="UTC")

#View(data)
summary(data)
      id              tweet_id             text               type           bookmark_count    favorite_count     retweet_count      reply_count      
 Length:11728       Length:11728       Length:11728       Length:11728       Min.   :  0.000   Min.   :    0.00   Min.   :   0.00   Min.   :   0.000  
 Class :character   Class :character   Class :character   Class :character   1st Qu.:  0.000   1st Qu.:    7.00   1st Qu.:   2.00   1st Qu.:   0.000  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character   Median :  0.000   Median :   20.00   Median :   5.00   Median :   1.000  
                                                                             Mean   :  1.543   Mean   :   60.67   Mean   :  10.62   Mean   :   3.888  
                                                                             3rd Qu.:  1.000   3rd Qu.:   57.00   3rd Qu.:  11.00   3rd Qu.:   3.000  
                                                                             Max.   :418.000   Max.   :41655.00   Max.   :4214.00   Max.   :2317.000  
                                                                                                                                                      
   view_count        created_at                       hashtags             urls            media_type         media_urls       
 Min.   :      5   Min.   :2022-08-01 03:05:11.00   Length:11728       Length:11728       Length:11728       Length:11728      
 1st Qu.:   2643   1st Qu.:2022-10-19 12:56:27.00   Class :character   Class :character   Class :character   Class :character  
 Median :   6240   Median :2023-01-29 08:26:30.00   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
 Mean   :  14182   Mean   :2023-01-30 07:39:34.96                                                                              
 3rd Qu.:  16058   3rd Qu.:2023-05-05 14:16:43.25                                                                              
 Max.   :7604544   Max.   :2023-08-31 20:50:01.00                                                                              
 NA's   :4840                                                                                                                  

Initial Data Preparation

# View of how many entries each HEI has
number_interactions <- data %>%
              group_by(id) %>% summarise(count = n())

number_interactions
# Since complutense only has 1 entry we can't learn anything from it, so we removed it
data <- data[data$id != "complutense.csv", ]
# Visualization of number all posts, just tweets and just replies
number_posts <- data %>%
              group_by(id) %>% summarise(count = n())

number_tweets <- data[data$type == "Tweet", ] %>%
              group_by(id) %>% summarise(count = n())

number_replies <- data[data$type == "Reply", ] %>%
              group_by(id) %>% summarise(count = n())

print(number_posts)
print(number_tweets)
print(number_replies)

Calculating the percentage of tweets and replies based on all posts

# Merging the counts of tweets (count.y) and replies (count) with the count of posts (count.x)
data_ratio <- merge(number_posts, number_tweets, by = "id", all = TRUE)
data_ratio <- merge(data_ratio, number_replies, by = "id", all = TRUE)


data_ratio$percentage_tweets <- (data_ratio$count.y / data_ratio$count.x) * 100
data_ratio$percentage_replies <- (data_ratio$count / data_ratio$count.x) * 100

data_ratio <- data_ratio[, c("id", "percentage_tweets", "percentage_replies")]

print(data_ratio)

For now we’ll be only looking at tweets

data_tweets <- data[data$type == "Tweet", ]

data_tweets

Function to calculate average posts

average_tweets <- function(timeframe = "days"){
  # Calculation of the timeframe between earliest and latest post for each HEI
  date_range <- data_tweets %>%
    group_by(id) %>%
    summarise(min_date = min(created_at),
              max_date = max(created_at)) %>%
    mutate(num_days = as.numeric(difftime(max_date, min_date, units = timeframe)))
  
  # Naming the column respecting the timeframe
  column_name <- paste0("avg_tweets_per_", timeframe)
  
  # Calculation of the number of tweets per day for each HEI
  tweets_per_timeframe <- number_tweets %>%
    left_join(date_range, by = "id") %>%
    mutate(!!column_name := count / num_days)
  
  print(tweets_per_timeframe)
  return(tweets_per_timeframe)
}
tweets_per_day <- average_tweets()
tweets_per_week <- average_tweets(timeframe = "weeks")

Plot for the average number of tweets per day for each HEI

barplot(tweets_per_day$avg_tweets_per_days,
        names.arg = tweets_per_day$id,
        main = "Average Tweets per Day",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(tweets_per_day$avg_tweets_per_days) + 1),
        las = 2,
        col = "#3498DB")

# Adding text labels over each bar and aligning it with the center of each bar 
text(x = barplot(tweets_per_day$avg_tweets_per_days, plot = FALSE),
     y = tweets_per_day$avg_tweets_per_days,
     labels = round(tweets_per_day$avg_tweets_per_days, 2),
     pos = 3)

Plot for the average number of tweets per week for each HEI

barplot(tweets_per_week$avg_tweets_per_weeks,
        names.arg = tweets_per_week$id,
        main = "Average Tweets per Week",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(tweets_per_week$avg_tweets_per_weeks) + 5),
        las = 2,
        col = "#E74C3C")

text(x = barplot(tweets_per_week$avg_tweets_per_weeks, plot = FALSE),
     y = tweets_per_week$avg_tweets_per_weeks,
     labels = round(tweets_per_week$avg_tweets_per_weeks, 2),
     pos = 3)

Defining the intervals of time for the academic year

intervals <- list(
  interval1 = as.POSIXct(c("2022-08-31", "2022-12-15")),
  interval2 = as.POSIXct(c("2023-01-04", "2023-04-01")),
  interval3 = as.POSIXct(c("2023-04-14", "2023-06-15"))
)

Function to check if a date falls within a given interval of time and apply appropriate Boolean

check_interval <- function(date) {
  for (i in 1:length(intervals)) {
    interval_start <- intervals[[i]][1]
    interval_end <- intervals[[i]][2]
    if (date >= interval_start & date <= interval_end) {
      return(TRUE)
    }
  }
  return(FALSE)
}
data_tweets$academic_year <- sapply(data_tweets$created_at, check_interval)
print(data.frame(id = data_tweets$id, academic_year = data_tweets$academic_year))

Plot for the number of tweets per timeframe of either vacation or academic time

barplot(table(data_tweets$academic_year),
        main = "Number of Tweets per Timeframe",
        xlab = "Time",
        ylab = "Count",
        ylim = c(0, max(table(data_tweets$academic_year)) + 1000),
        names.arg = c("Vacation", "Academic"),
        col = c("#8E44AD", "#F1C40F"))

text(x = barplot(data_tweets$academic_year, plot = FALSE), 
     y = table(data_tweets$academic_year) + 0.5, 
     labels = table(data_tweets$academic_year), 
     pos = 3)

Function to count number of tweets and average per day

analyze_tweets <- function(academic_year_filter = TRUE) {
  # Filtering the data based on the academic_year_filter
  filtered_data <- data_tweets %>%
    filter(academic_year == academic_year_filter)
  
  # Count of days for each HEI
  unique_days <- filtered_data %>%
    group_by(id) %>%
    summarise(unique_days = n_distinct(as.Date(created_at)))
  
  # Count of tweets for each id
  number_tweets_boolean <- filtered_data %>%
    group_by(id) %>%
    summarise(count = n())
  
  # Naming the column respecting the time period
  year <- ifelse(academic_year_filter, "academic_time", "vacation_time")
  column_name <- paste0("avg_tweets_in_", year)
  
  # Combination of data and calculation of average posts per day
  combined_data <- left_join(unique_days, number_tweets_boolean, by = "id")
  combined_data <- combined_data %>%
    mutate(!!column_name := count / unique_days)
  
  print(combined_data)
  return(combined_data)
}
data_tweets_academic <- analyze_tweets()
data_tweets_vacations <- analyze_tweets(academic_year_filter = FALSE)

Plot for the average number of tweets during academic time for each HEI

barplot(data_tweets_academic$avg_tweets_in_academic_time,
        names.arg = data_tweets_academic$id,
        main = "Average Tweets during Academic Time",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(data_tweets_academic$avg_tweets_in_academic_time) + 5),
        las = 2,
        col = "#34495E")

text(x = barplot(data_tweets_academic$avg_tweets_in_academic_time, plot = FALSE),
     y = data_tweets_academic$avg_tweets_in_academic_time,
     labels = round(data_tweets_academic$avg_tweets_in_academic_time, 2),
     pos = 3)

Plot for the average number of tweets during vacation time for each HEI

barplot(data_tweets_vacations$avg_tweets_in_vacation_time,
        names.arg = data_tweets_vacations$id,
        main = "Average Tweets during Vacation Time",
        xlab = "HEI",
        ylab = "Average Number of Tweets",
        ylim = c(0, max(data_tweets_vacations$avg_tweets_in_vacation_time) + 5),
        las = 2,
        col = "#D35400")

text(x = barplot(data_tweets_vacations$avg_tweets_in_vacation_time, plot = FALSE),
     y = data_tweets_vacations$avg_tweets_in_vacation_time,
     labels = round(data_tweets_vacations$avg_tweets_in_vacation_time, 2),
     pos = 3)

Data preparation for day of the week

# Creating new table that contains a new column for the day of the week
data_tweets_days <- data_tweets %>%
  mutate(day_of_week = weekdays(created_at))

# Selecting only the id, created_at, and day_of_week columns for the new table
data_tweets_days <- data_tweets_days %>%
  select(id, created_at, day_of_week)

print(data_tweets_days)
# Grouping by id and day_of_week, then counting the number of tweets
number_tweets_days <- data_tweets_days %>%
  group_by(id, day_of_week) %>%
  summarise(count = n())
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
# Grouping by id, day_of_week and day created at, then counting the number of tweets
number_tweets_per_day <- data_tweets_days %>%
  mutate(created_date = as.Date(created_at)) %>%
  group_by(id, day_of_week, created_date) %>%
  summarise(count = n())
`summarise()` has grouped output by 'id', 'day_of_week'. You can override using the `.groups` argument.
# Finding for each HEI the average count of tweets per day
average_number_tweets_per_day <- number_tweets_per_day %>%
  group_by(id, day_of_week) %>%
  summarise(average_count = mean(count))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
print(number_tweets_days)

Highest and lowest tweets

# Finding the HEI with the lowest count of tweets per day
lowest_count <- number_tweets_days %>%
  group_by(day_of_week) %>%
  slice_min(order_by = count) %>%
  select(day_of_week, id, count)

# Same but highest count of tweets per day
highest_count <- number_tweets_days %>%
  group_by(day_of_week) %>%
  slice_max(order_by = count) %>%
  select(day_of_week, id, count)

# Combine the results
high_low_HEI <- bind_rows(lowest_count, highest_count) %>%
  arrange(day_of_week)

print(high_low_HEI)

Plot for the lowest and highest count of tweets per day for each day of the week

ggplot(high_low_HEI, aes(x = day_of_week, y = count, fill = id)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = count),
            position = position_dodge(width = 0.9),
            vjust = -0.5,
            size = 3) +
  labs(title = "Lowest and Highest Count of Tweets per Day for Each Day of the Week",
       x = "Day of the Week", y = "Count") +
  scale_fill_manual(values = rainbow(length(unique(high_low_HEI$id)))) +
  theme_minimal() +
  theme(legend.title = element_blank())

Average of tweets

# Finding the HEI with lowest and highest averaged count of tweets per day
high_low_average_HEIs <- average_number_tweets_per_day %>%
  group_by(day_of_week) %>%
  filter(average_count == max(average_count) | average_count == min(average_count)) %>%
  arrange(day_of_week, ifelse(average_count == min(average_count), average_count, -average_count))

print(high_low_average_HEIs)

Plot for the highest and lowest average count of tweets per day for each day of the week

ggplot(high_low_average_HEIs, aes(x = day_of_week, y = average_count, fill = id)) +
  geom_bar(stat = "identity", position = "dodge") +
  geom_text(aes(label = round(average_count, 2)),
            position = position_dodge(width = 0.7),
            vjust = -0.5,
            size = 3) +
  labs(title = "Highest and Lowest Average Count of Tweets per Day for Each Day of the Week",
       x = "Day of the Week", y = "Average Count") +
  scale_fill_manual(values = rainbow(length(unique(high_low_HEI$id)))) +
  theme_minimal() +
  theme(legend.title = element_blank())

Views Likes Retweets and Replies

# Table containing views, likes, retweets and replies for each media type for each HEI
types_of_tweets <- data_tweets %>%
              group_by(id, media_type) %>%
              summarise(count = n(),
                        views = sum(view_count, na.rm = TRUE),
                        likes = sum(favorite_count, na.rm = TRUE),
                        retweets = sum(retweet_count, na.rm = TRUE),
                        replies = sum(reply_count, na.rm = TRUE))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
                        
print(types_of_tweets)                        
# Grouping by HEI and calculating the total values of views, likes and replies across all media types
total_tweets_stats <- types_of_tweets %>%
  group_by(id) %>%
  summarise(total_views = sum(views),
            total_likes = sum(likes),
            total_replies = sum(replies))

print(total_tweets_stats)

Function for piechart creation for views, likes and replies

pie_maker <- function(target_id = "duke.csv"){
  # Filtering data for the specific HEI
  hei_data <- types_of_tweets %>%
    filter(id == target_id)
  
  # Calculate total views for each media type for the specific ID
  hei_media <- hei_data %>%
    group_by(media_type) %>%
    summarise(total_views = sum(views),
              total_likes = sum(likes),
              total_replies = sum(replies))
  
  # Calculating the percentage of views for each media type for the specific ID
  hei_media$percentage_view <- hei_media$total_views / sum(hei_media$total_views) * 100
  hei_media$percentage_like <- hei_media$total_likes / sum(hei_media$total_likes) * 100
  hei_media$percentage_reply <- hei_media$total_replies / sum(hei_media$total_replies) * 100
  
  # Creating the pie chart for views
  hei_pie_chart_views <- ggplot(hei_media, aes(x = "", y = percentage_view, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_views, "(", round(percentage_view, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#2196F3", "animated_gif" = "#E67E22", "photo" = "#8E44AD", "video" = "#138D75")) +
    labs(title = paste("Views for each media type -", target_id))
  
  # Creating the pie chart for likes
  hei_pie_chart_likes <- ggplot(hei_media, aes(x = "", y = percentage_like, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_likes, "(", round(percentage_like, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#E91E63", "animated_gif" = "#4A148C", "photo" = "#90CAF9", "video" = "#00BFA5")) +
    labs(title = paste("Likes for each media type -", target_id))
  
  # Creating the pie chart for replies
  hei_pie_chart_replies <- ggplot(hei_media, aes(x = "", y = percentage_reply, fill = media_type)) +
    geom_bar(stat = "identity", width = 1) +
    coord_polar("y", start = 0) +
    theme_void() +
    theme(legend.position = "right") +
    geom_text(aes(label = paste(media_type, "\n", total_replies, "(", round(percentage_reply, 1), "%)")), position = position_stack(vjust = 0.5), color = "#FFFFFF") +
    scale_fill_manual(values = c("no_media" = "#666600", "animated_gif" = "#99CCCC", "photo" = "#9966CC", "video" = "#330000")) +
    labs(title = paste("Replies for each media type -", target_id))
  
  # Print the pie charts
  print(hei_pie_chart_views)
  print(hei_pie_chart_likes)
  print(hei_pie_chart_replies)
}

Plot of piecharts for each HEI

pie_maker()

pie_maker("epfl.csv")

pie_maker("goe.csv")

pie_maker("harvard.csv")

pie_maker("leicester.csv")

pie_maker("manchester.csv")

pie_maker("mit.csv")

pie_maker("sb.csv")

pie_maker("stanford.csv")

pie_maker("trinity.csv")

pie_maker("wv.csv")

pie_maker("yale.csv")

# Calculation of like_ratio and replies_ratio percentages
ratios_tweets_table <- total_tweets_stats %>%
  mutate(like_ratio = total_likes / total_views * 100,
         replies_ratio = total_replies / total_views * 100)

# Creation of new table with each HEI, like_ratio, and replies_ratio 
hei_tweets_ratios <- ratios_tweets_table %>%
  select(id, like_ratio, replies_ratio) %>%
  distinct()

print(hei_tweets_ratios)

Plot for like_ratio and replies_ratio for each HEI

ggplot(hei_tweets_ratios, aes(x = id)) +
  geom_bar(aes(y = like_ratio, fill = "Like Ratio"), stat = "identity", position = "dodge") +
  geom_bar(aes(y = replies_ratio, fill = "Replys Ratio"), stat = "identity", position = "dodge") +
  geom_text(aes(y = like_ratio, label = round(like_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#000000") +
  geom_text(aes(y = replies_ratio, label = round(replies_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#FFFFFF") +
  labs(title = "Like and Replys Ratios by HEI",
       x = "HEI",
       y = "Ratio (%)",
       fill = "Metric") +
  scale_fill_manual(values = c("Like Ratio" = "#2196F3", "Replys Ratio" = "#F44336")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

# Table with averages of views, likes, retweets and replies
types_of_tweets_per_tweet <- types_of_tweets %>%
                        group_by(id, media_type) %>%
                        summarise(avg_views = mean(views / count),
                                  avg_likes = mean(likes / count),
                                  avg_retweets = mean(retweets / count),
                                  avg_replies = mean(replies / count))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
print(types_of_tweets_per_tweet)
# Grouping by HEI and calculating the average values of views, likes and replies across all media types
total_average_stats <- types_of_tweets_per_tweet %>%
  group_by(id) %>%
  summarise(avg_views = sum(avg_views),
            avg_likes = sum(avg_likes),
            avg_replies = sum(avg_replies))

print(total_average_stats)
# Calculation of like_ratio and replies_ratio percentages
ratios_average_table <- total_average_stats %>%
  mutate(like_ratio = avg_likes / avg_views * 100,
         replies_ratio = avg_replies / avg_views * 100)

# Creation of new table with each HEI, like_ratio, and replies_ratio 
hei_average_ratios <- ratios_average_table %>%
  select(id, like_ratio, replies_ratio) %>%
  distinct()

print(hei_average_ratios)

Plot for like_ratio and replies_ratio for each HEI

ggplot(hei_average_ratios, aes(x = id)) +
  geom_bar(aes(y = like_ratio, fill = "Like Ratio"), stat = "identity", position = "dodge") +
  geom_bar(aes(y = replies_ratio, fill = "Replies Ratio"), stat = "identity", position = "dodge") +
  geom_text(aes(y = like_ratio, label = round(like_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#000000") +
  geom_text(aes(y = replies_ratio, label = round(replies_ratio, 2)), vjust = -0.5, position = position_dodge(width = 0.9), size = 3, color = "#FFFFFF") +
  labs(title = "Like and Replies Ratios by HEI",
       x = "HEI",
       y = "Ratio (%)",
       fill = "Metric") +
  scale_fill_manual(values = c("Like Ratio" = "#330066", "Replies Ratio" = "#FF6666")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Hashtags

# Table with number of unique hashtags
unique_hashtags <- data_tweets %>%
                group_by(id) %>%
                summarise(count = n(),
                          unique_hashtags = length(unique(hashtags)))

print(unique_hashtags)

Plot for the count of unique hashtags for each HEI

barplot(unique_hashtags$unique_hashtags,
        names.arg = unique_hashtags$id,
        main = "Unique Hashtags for Each HEI",
        xlab = "HEI",
        ylab = "Count of Unique Hashtags",
        ylim = c(0, max(unique_hashtags$unique_hashtags) + 50),
        las = 2,
        col= "#16A085")

text(x = barplot(unique_hashtags$unique_hashtags, plot = FALSE),
     y = unique_hashtags$unique_hashtags,
     labels = round(unique_hashtags$unique_hashtags, 2),
     pos = 3)

Heatmaps

# Create column hour from created_at
data_tweets_days$created_hour <- as.numeric(format(data_tweets_days$created_at, "%H"))

Function to plot heatmap for various HEIs

heatmap_maker <- function(target_id = "duke.csv"){
  # Filtering data for the specific HEI
  target_data <- data_tweets_days %>%
    filter(id == target_id)
  
  # Grouping by day of the week and hour, and counting the number of tweets
  tweet_counts <- target_data %>%
    group_by(day_of_week, created_hour) %>%
    summarise(num_tweets = n())
  
  # Plotting heatmap
  ggplot(tweet_counts, aes(x = day_of_week, y = created_hour, fill = num_tweets)) +
    geom_tile() +
    scale_fill_gradient(low = "white", high = "blue") +
    labs(title = paste("Tweet Heatmap for", target_id),
         x = "Day of the week",
         y = "Hour of the day")
}

heatmap_maker()
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("epfl.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("goe.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("harvard.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("leicester.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("manchester.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("mit.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("sb.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("stanford.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("trinity.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("wv.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

heatmap_maker("yale.csv")
`summarise()` has grouped output by 'day_of_week'. You can override using the `.groups` argument.

Text

data_tweets_content <- data_tweets %>%
            select(id, text)

# Counting number of words
data_tweets_content <- data_tweets_content %>%
  mutate(num_words = lengths(strsplit(text, "\\s+")))

print(data_tweets_content)

# Grouping by HEI and calculate average, minimum, and maximum values of number of words
data_tweets_content_metrics <- data_tweets_content %>%
  group_by(id) %>%
  summarise(average_num_words = mean(num_words),
            min_num_words = min(num_words),
            max_num_words = max(num_words))
print(data_tweets_content_metrics)

Plot for the average, maximum and minimum values of words for each HEI

ggplot(data_tweets_content_metrics, aes(x = id, y = average_num_words)) +
  geom_point(aes(color = "Average")) +
  geom_errorbar(aes(ymin = min_num_words, ymax = max_num_words, color = "Range"), width = 0.2) +
  scale_color_manual(values = c("Average" = "#1976D2", "Range" = "#EF5350")) +
  labs(title = "Word Count Summary by HEI",
       x = "HEI",
       y = "Number of Words",
       color = "Metric") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))

Now comments

data_replies <- data[data$type == "Reply", ]

data_replies

Interactions to replies

# Table containing views, likes, retweets and replies for each media type for each HEI
types_of_replies <- data_replies %>%
              group_by(id, media_type) %>%
              summarise(count = n(),
                        views = sum(view_count, na.rm = TRUE),
                        likes = sum(favorite_count, na.rm = TRUE),
                        retweets = sum(retweet_count, na.rm = TRUE),
                        replies = sum(reply_count, na.rm = TRUE))
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument.
                        
print(types_of_replies)                        
# Grouping by HEI and calculating the total values of views, likes and replies across all media types
total_replies_stats <- types_of_replies %>%
  group_by(id) %>%
  summarise(total_views = sum(views),
            total_likes = sum(likes),
            total_replies = sum(replies))

print(total_replies_stats)
# Calculation of like_ratio and replies_ratio percentages
ratios_replies_table <- total_replies_stats %>%
  mutate(like_ratio = total_likes / total_views * 100,
         replies_ratio = total_replies / total_views * 100)

# Creation of new table with each HEI, like_ratio, and replies_ratio 
hei_replies_ratios <- ratios_replies_table %>%
  select(id, like_ratio, replies_ratio) %>%
  distinct()

print(hei_replies_ratios)

Clusters

# Creating table for cluster algorithms

# Joining attribute count (number of tweets) and unique_hashtags (number of unique hashtags) per HEI
cluster_table <- merge(select(unique_hashtags, id, unique_hashtags), select(number_tweets, id, count), by = "id", all=TRUE)

# Joining attribute avg_tweets_per_days (average of tweets per day) per HEI
cluster_table <- merge(cluster_table, select(tweets_per_day, id, avg_tweets_per_days), by = "id", all=TRUE)

# Joining attribute avg_tweets_per_weeks (average of tweets per week) per HEI
cluster_table <- merge(cluster_table, select(tweets_per_week, id, avg_tweets_per_weeks), by = "id", all=TRUE)

# Joining attribute avg_tweets_in_academic_time (average of tweets during academic time) per HEI
cluster_table <- merge(cluster_table, select(data_tweets_academic, id, avg_tweets_in_academic_time), by = "id", all=TRUE)

# Joining attribute avg_tweets_in_vacation_time (average of tweets during vacation time) per HEI
cluster_table <- merge(cluster_table, select(data_tweets_vacations, id, avg_tweets_in_vacation_time), by = "id", all=TRUE)

# Joining attribute total_views (total number of views), total_likes (total number of likes) and total_replies (total number of replies) per HEI
cluster_table <- merge(cluster_table, select(total_tweets_stats, id, total_views, total_likes, total_replies), by = "id", all=TRUE)

# Joining attribute like_ratio (ratio of total number of likes) and replies_ratio (ratio of total number of replies) per HEI
cluster_table <- merge(cluster_table, select(hei_tweets_ratios, id, like_ratio, replies_ratio), by = "id", all=TRUE)
cluster_table <- cluster_table %>%
  rename(total_like_ratio = like_ratio, 
         total_replies_ratio = replies_ratio)

# Joining attribute avg_views (average number of views), avg_likes (average number of likes) and avg_replies (average number of replies) per HEI
cluster_table <- merge(cluster_table, select(total_average_stats, id, avg_views, avg_likes, avg_replies), by = "id", all=TRUE)

# Joining attribute like_ratio (ratio of average number of likes) and replies_ratio (ratio of average number of replies) per HEI
cluster_table <- merge(cluster_table, select(hei_average_ratios, id, like_ratio, replies_ratio), by = "id", all=TRUE)
cluster_table <- cluster_table %>%
  rename(avg_like_ratio = like_ratio, 
         avg_replies_ratio = replies_ratio)

print(cluster_table)

Function for cluster method

cluster_maker <- function(seed = 123, num_clusters = 3, table){
  set.seed(123)
  
  # Excluding id column for clustering
  cluster_data <- select(table, -id)
  
  # Scaling the data for kmeans method
  scaled_data <- scale(cluster_data)
  
  kmeans_result <- kmeans(scaled_data, centers = num_clusters)
  
  print(kmeans_result$centers)
  print(kmeans_result$cluster)
  
  return(kmeans_result)
}

Function to add ids to better visualize results

cluster_id_maker <- function(kmeans_result, table){
  # Merging the cluster assignments with the original data
  cluster_assignments <- data.frame(id = table$id, cluster = kmeans_result$cluster)

  print(cluster_assignments)
  plot(kmeans_result$cluster)
}

Three clusters with seed 123

cluster_123_3 <- cluster_maker(table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.6381349 -0.5376116          -0.5344623           -0.5344623                  -0.5093965                  -0.5929981  -0.3102091  -0.1331381
2      -0.3232547  1.3319287           1.3324807            1.3324807                   1.2897365                   1.3561417   1.2841600   1.2437001
3       0.4806948 -0.3971585          -0.3990092           -0.3990092                  -0.3901700                  -0.3815718  -0.4869754  -0.5552810
  total_replies total_like_ratio total_replies_ratio   avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1   -0.03547406        1.3168443          1.14394012 -0.04762103  0.5778107   0.5798181      1.4433344        1.09974566
2    1.11229258       -0.2089453          0.07129591  1.21230324  0.7827831   0.6292289     -0.3283523       -0.01971601
3   -0.53840926       -0.5539495         -0.60761801 -0.58234111 -0.6802969  -0.6045235     -0.5574910       -0.54001482
 [1] 1 3 3 2 3 3 2 3 1 3 1 2
cluster_id_maker(cluster_123_3, table = cluster_table)

Six clusters with seed 123

cluster_123_6 <- cluster_maker(num_clusters = 6, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.4103492 -1.3031254          -1.3032034           -1.3032034                  -1.2416625                  -1.0231249  -0.6375984 -0.66179186
2      -0.3366538  2.0980363           2.1001655            2.1001655                   2.2152351                   2.2141794   2.7698115  2.76526795
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.2999826 -0.35327890
4      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.2090329  0.07640829
5      -0.2808240 -0.5708690          -0.5746071           -0.5746071                  -0.5989595                  -0.6377948  -0.5055879 -0.58466068
6      -0.6269689 -0.1034839          -0.1067369           -0.1067369                  -0.1791121                  -0.2061612   0.1645105  0.21131146
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1   -0.61859306        0.9061875         -0.73455883 -0.6997429 -0.7372601 -0.68389426     -0.2923006       -0.90543423
2    2.91587745       -0.1894786          0.24320057  2.1085449  1.4654190  1.59191180     -0.3555917        0.18423794
3   -0.31932304       -0.5257982         -0.10316261 -0.5364867 -0.6301142 -0.54350287     -0.5247867       -0.25353926
4    0.55340665        1.7400189          2.73035156  0.4273143  1.9304181  2.31424168      2.6027601        2.94250000
5   -0.55967675       -0.9230136         -0.72436706 -0.5414191 -0.6611809 -0.58166179     -0.5983170       -0.51410929
6   -0.07123056        0.6299026          0.08119858  0.4658671  0.4051027  0.05107825      0.4714812        0.02721398
 [1] 6 5 1 2 3 5 6 5 4 3 6 3
cluster_id_maker(cluster_123_6, table = cluster_table)

Four clusters with seed 4855

cluster_123_3 <- cluster_maker(seed = 4855, num_clusters = 4, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.3132053 -0.7539331          -0.7567562           -0.7567562                  -0.7596352                  -0.7341273  -0.5385905  -0.6039435
2      -0.4672956  1.2357198           1.2354314            1.2354314                   1.1911790                   1.2741657   1.9924687   1.9375124
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.2999826  -0.3532789
4      -0.6381349 -0.5376116          -0.5344623           -0.5344623                  -0.5093965                  -0.5929981  -0.3102091  -0.1331381
  total_replies total_like_ratio total_replies_ratio   avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1   -0.57440583       -0.4657134          -0.7269150 -0.58100004 -0.6802007  -0.6072199     -0.5218129       -0.61194052
2    1.68100730       -0.2551425          -0.1073362  2.03816171  1.4388566   1.1599670     -0.3341956       -0.04542855
3   -0.31932304       -0.5257982          -0.1031626 -0.53648673 -0.6301142  -0.5435029     -0.5247867       -0.25353926
4   -0.03547406        1.3168443           1.1439401 -0.04762103  0.5778107   0.5798181      1.4433344        1.09974566
 [1] 4 1 1 2 3 1 2 1 4 3 4 3
cluster_id_maker(cluster_123_3, table = cluster_table)

Six clusters with seed 4855

cluster_123_6 <- cluster_maker(seed = 4855, num_clusters = 6, table = cluster_table)
  unique_hashtags      count avg_tweets_per_days avg_tweets_per_weeks avg_tweets_in_academic_time avg_tweets_in_vacation_time total_views total_likes
1      -0.4103492 -1.3031254          -1.3032034           -1.3032034                  -1.2416625                  -1.0231249  -0.6375984 -0.66179186
2      -0.3366538  2.0980363           2.1001655            2.1001655                   2.2152351                   2.2141794   2.7698115  2.76526795
3       1.3672724  0.7190425           0.7198496            0.7198496                   0.7281241                   0.7223908  -0.2999826 -0.35327890
4      -0.6314353 -0.9289798          -0.9124788           -0.9124788                  -0.8237303                  -0.8263586  -0.2090329  0.07640829
5      -0.2808240 -0.5708690          -0.5746071           -0.5746071                  -0.5989595                  -0.6377948  -0.5055879 -0.58466068
6      -0.6269689 -0.1034839          -0.1067369           -0.1067369                  -0.1791121                  -0.2061612   0.1645105  0.21131146
  total_replies total_like_ratio total_replies_ratio  avg_views  avg_likes avg_replies avg_like_ratio avg_replies_ratio
1   -0.61859306        0.9061875         -0.73455883 -0.6997429 -0.7372601 -0.68389426     -0.2923006       -0.90543423
2    2.91587745       -0.1894786          0.24320057  2.1085449  1.4654190  1.59191180     -0.3555917        0.18423794
3   -0.31932304       -0.5257982         -0.10316261 -0.5364867 -0.6301142 -0.54350287     -0.5247867       -0.25353926
4    0.55340665        1.7400189          2.73035156  0.4273143  1.9304181  2.31424168      2.6027601        2.94250000
5   -0.55967675       -0.9230136         -0.72436706 -0.5414191 -0.6611809 -0.58166179     -0.5983170       -0.51410929
6   -0.07123056        0.6299026          0.08119858  0.4658671  0.4051027  0.05107825      0.4714812        0.02721398
 [1] 6 5 1 2 3 5 6 5 4 3 6 3
cluster_id_maker(cluster_123_6, table = cluster_table)

LS0tCnRpdGxlOiAiUHJvamVjdCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQojIFRPRE8gdHJhdGFyIGRlIE5BcyBlIHBhc3NhciB0YWJlbGFzIHByYSBjc3YKIyBIb3cgdG8gZ2l2ZSBuYW1lIHRvIG1lZGlhCgojIyMgRm9yIFIgYmVnaW5uZXJzCk5ldyBjaHVuayAqQ3RybCtBbHQrSSoKCkV4ZWN1dGUgY2h1bmsgKkN0cmwrU2hpZnQrRW50ZXIqCgpFeGVjdXRlIGFsbCBjaHVua3MgKkN0cmwrQWx0K1IqCgpIVE1MIHByZXZpZXcgKkN0cmwrU2hpZnQrSyoKCiMgTGlicmFyeSBwcmVwYXJhdGlvbnMKCmBgYHtyfQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KHN0YXRzKQpgYGAKCiMgRGF0YSBJbXBvcnQKCmBgYHtyfQpkYXRhIDwtIHJlYWQuY3N2KCJ+LzR5ZWFyLzJzZW1lc3Rlci9kdElJL0NTVnMvSEVJcy5jc3YiLAogICAgICAgICAgICAgICAgIGNvbENsYXNzZXMgPSBjKHR3ZWV0X2lkID0gImNoYXJhY3RlciIpKQoKIyBNb2RpZnlpbmcgY3JlYXRlZF9hdCB0eXBlIHNvIHRoYXQgYXR0cmlidXRlIGNhbiBiZSB1c2VkIG1vcmUgZWFzaWx5IApkYXRhJGNyZWF0ZWRfYXQgPC0gYXMuUE9TSVhjdChkYXRhJGNyZWF0ZWRfYXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdD0gIiVZLSVtLSVkVCVIOiVNOiVTIiwgdHo9IlVUQyIpCgojVmlldyhkYXRhKQpzdW1tYXJ5KGRhdGEpCmBgYAoKIyBJbml0aWFsIERhdGEgUHJlcGFyYXRpb24KCmBgYHtyfQojIFZpZXcgb2YgaG93IG1hbnkgZW50cmllcyBlYWNoIEhFSSBoYXMKbnVtYmVyX2ludGVyYWN0aW9ucyA8LSBkYXRhICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkKSAlPiUgc3VtbWFyaXNlKGNvdW50ID0gbigpKQoKbnVtYmVyX2ludGVyYWN0aW9ucwpgYGAKCmBgYHtyfQojIFNpbmNlIGNvbXBsdXRlbnNlIG9ubHkgaGFzIDEgZW50cnkgd2UgY2FuJ3QgbGVhcm4gYW55dGhpbmcgZnJvbSBpdCwgc28gd2UgcmVtb3ZlZCBpdApkYXRhIDwtIGRhdGFbZGF0YSRpZCAhPSAiY29tcGx1dGVuc2UuY3N2IiwgXQpgYGAKCmBgYHtyfQojIFZpc3VhbGl6YXRpb24gb2YgbnVtYmVyIGFsbCBwb3N0cywganVzdCB0d2VldHMgYW5kIGp1c3QgcmVwbGllcwpudW1iZXJfcG9zdHMgPC0gZGF0YSAlPiUKICAgICAgICAgICAgICBncm91cF9ieShpZCkgJT4lIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKCm51bWJlcl90d2VldHMgPC0gZGF0YVtkYXRhJHR5cGUgPT0gIlR3ZWV0IiwgXSAlPiUKICAgICAgICAgICAgICBncm91cF9ieShpZCkgJT4lIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKCm51bWJlcl9yZXBsaWVzIDwtIGRhdGFbZGF0YSR0eXBlID09ICJSZXBseSIsIF0gJT4lCiAgICAgICAgICAgICAgZ3JvdXBfYnkoaWQpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpwcmludChudW1iZXJfcG9zdHMpCnByaW50KG51bWJlcl90d2VldHMpCnByaW50KG51bWJlcl9yZXBsaWVzKQpgYGAKCiMgQ2FsY3VsYXRpbmcgdGhlIHBlcmNlbnRhZ2Ugb2YgdHdlZXRzIGFuZCByZXBsaWVzIGJhc2VkIG9uIGFsbCBwb3N0cwoKYGBge3J9CiMgTWVyZ2luZyB0aGUgY291bnRzIG9mIHR3ZWV0cyAoY291bnQueSkgYW5kIHJlcGxpZXMgKGNvdW50KSB3aXRoIHRoZSBjb3VudCBvZiBwb3N0cyAoY291bnQueCkKZGF0YV9yYXRpbyA8LSBtZXJnZShudW1iZXJfcG9zdHMsIG51bWJlcl90d2VldHMsIGJ5ID0gImlkIiwgYWxsID0gVFJVRSkKZGF0YV9yYXRpbyA8LSBtZXJnZShkYXRhX3JhdGlvLCBudW1iZXJfcmVwbGllcywgYnkgPSAiaWQiLCBhbGwgPSBUUlVFKQoKCmRhdGFfcmF0aW8kcGVyY2VudGFnZV90d2VldHMgPC0gKGRhdGFfcmF0aW8kY291bnQueSAvIGRhdGFfcmF0aW8kY291bnQueCkgKiAxMDAKZGF0YV9yYXRpbyRwZXJjZW50YWdlX3JlcGxpZXMgPC0gKGRhdGFfcmF0aW8kY291bnQgLyBkYXRhX3JhdGlvJGNvdW50LngpICogMTAwCgpkYXRhX3JhdGlvIDwtIGRhdGFfcmF0aW9bLCBjKCJpZCIsICJwZXJjZW50YWdlX3R3ZWV0cyIsICJwZXJjZW50YWdlX3JlcGxpZXMiKV0KCnByaW50KGRhdGFfcmF0aW8pCmBgYAoKIyBGb3Igbm93IHdlJ2xsIGJlIG9ubHkgbG9va2luZyBhdCB0d2VldHMKCmBgYHtyfQpkYXRhX3R3ZWV0cyA8LSBkYXRhW2RhdGEkdHlwZSA9PSAiVHdlZXQiLCBdCgpkYXRhX3R3ZWV0cwpgYGAKCgojIEZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBhdmVyYWdlIHBvc3RzCgpgYGB7cn0KYXZlcmFnZV90d2VldHMgPC0gZnVuY3Rpb24odGltZWZyYW1lID0gImRheXMiKXsKICAjIENhbGN1bGF0aW9uIG9mIHRoZSB0aW1lZnJhbWUgYmV0d2VlbiBlYXJsaWVzdCBhbmQgbGF0ZXN0IHBvc3QgZm9yIGVhY2ggSEVJCiAgZGF0ZV9yYW5nZSA8LSBkYXRhX3R3ZWV0cyAlPiUKICAgIGdyb3VwX2J5KGlkKSAlPiUKICAgIHN1bW1hcmlzZShtaW5fZGF0ZSA9IG1pbihjcmVhdGVkX2F0KSwKICAgICAgICAgICAgICBtYXhfZGF0ZSA9IG1heChjcmVhdGVkX2F0KSkgJT4lCiAgICBtdXRhdGUobnVtX2RheXMgPSBhcy5udW1lcmljKGRpZmZ0aW1lKG1heF9kYXRlLCBtaW5fZGF0ZSwgdW5pdHMgPSB0aW1lZnJhbWUpKSkKICAKICAjIE5hbWluZyB0aGUgY29sdW1uIHJlc3BlY3RpbmcgdGhlIHRpbWVmcmFtZQogIGNvbHVtbl9uYW1lIDwtIHBhc3RlMCgiYXZnX3R3ZWV0c19wZXJfIiwgdGltZWZyYW1lKQogIAogICMgQ2FsY3VsYXRpb24gb2YgdGhlIG51bWJlciBvZiB0d2VldHMgcGVyIGRheSBmb3IgZWFjaCBIRUkKICB0d2VldHNfcGVyX3RpbWVmcmFtZSA8LSBudW1iZXJfdHdlZXRzICU+JQogICAgbGVmdF9qb2luKGRhdGVfcmFuZ2UsIGJ5ID0gImlkIikgJT4lCiAgICBtdXRhdGUoISFjb2x1bW5fbmFtZSA6PSBjb3VudCAvIG51bV9kYXlzKQogIAogIHByaW50KHR3ZWV0c19wZXJfdGltZWZyYW1lKQogIHJldHVybih0d2VldHNfcGVyX3RpbWVmcmFtZSkKfQpgYGAKCmBgYHtyfQp0d2VldHNfcGVyX2RheSA8LSBhdmVyYWdlX3R3ZWV0cygpCnR3ZWV0c19wZXJfd2VlayA8LSBhdmVyYWdlX3R3ZWV0cyh0aW1lZnJhbWUgPSAid2Vla3MiKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHR3ZWV0cyBwZXIgZGF5IGZvciBlYWNoIEhFSQoKYGBge3J9CmJhcnBsb3QodHdlZXRzX3Blcl9kYXkkYXZnX3R3ZWV0c19wZXJfZGF5cywKICAgICAgICBuYW1lcy5hcmcgPSB0d2VldHNfcGVyX2RheSRpZCwKICAgICAgICBtYWluID0gIkF2ZXJhZ2UgVHdlZXRzIHBlciBEYXkiLAogICAgICAgIHhsYWIgPSAiSEVJIiwKICAgICAgICB5bGFiID0gIkF2ZXJhZ2UgTnVtYmVyIG9mIFR3ZWV0cyIsCiAgICAgICAgeWxpbSA9IGMoMCwgbWF4KHR3ZWV0c19wZXJfZGF5JGF2Z190d2VldHNfcGVyX2RheXMpICsgMSksCiAgICAgICAgbGFzID0gMiwKICAgICAgICBjb2wgPSAiIzM0OThEQiIpCgojIEFkZGluZyB0ZXh0IGxhYmVscyBvdmVyIGVhY2ggYmFyIGFuZCBhbGlnbmluZyBpdCB3aXRoIHRoZSBjZW50ZXIgb2YgZWFjaCBiYXIgCnRleHQoeCA9IGJhcnBsb3QodHdlZXRzX3Blcl9kYXkkYXZnX3R3ZWV0c19wZXJfZGF5cywgcGxvdCA9IEZBTFNFKSwKICAgICB5ID0gdHdlZXRzX3Blcl9kYXkkYXZnX3R3ZWV0c19wZXJfZGF5cywKICAgICBsYWJlbHMgPSByb3VuZCh0d2VldHNfcGVyX2RheSRhdmdfdHdlZXRzX3Blcl9kYXlzLCAyKSwKICAgICBwb3MgPSAzKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHR3ZWV0cyBwZXIgd2VlayBmb3IgZWFjaCBIRUkKCmBgYHtyfQpiYXJwbG90KHR3ZWV0c19wZXJfd2VlayRhdmdfdHdlZXRzX3Blcl93ZWVrcywKICAgICAgICBuYW1lcy5hcmcgPSB0d2VldHNfcGVyX3dlZWskaWQsCiAgICAgICAgbWFpbiA9ICJBdmVyYWdlIFR3ZWV0cyBwZXIgV2VlayIsCiAgICAgICAgeGxhYiA9ICJIRUkiLAogICAgICAgIHlsYWIgPSAiQXZlcmFnZSBOdW1iZXIgb2YgVHdlZXRzIiwKICAgICAgICB5bGltID0gYygwLCBtYXgodHdlZXRzX3Blcl93ZWVrJGF2Z190d2VldHNfcGVyX3dlZWtzKSArIDUpLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sID0gIiNFNzRDM0MiKQoKdGV4dCh4ID0gYmFycGxvdCh0d2VldHNfcGVyX3dlZWskYXZnX3R3ZWV0c19wZXJfd2Vla3MsIHBsb3QgPSBGQUxTRSksCiAgICAgeSA9IHR3ZWV0c19wZXJfd2VlayRhdmdfdHdlZXRzX3Blcl93ZWVrcywKICAgICBsYWJlbHMgPSByb3VuZCh0d2VldHNfcGVyX3dlZWskYXZnX3R3ZWV0c19wZXJfd2Vla3MsIDIpLAogICAgIHBvcyA9IDMpCmBgYAoKIyBEZWZpbmluZyB0aGUgaW50ZXJ2YWxzIG9mIHRpbWUgZm9yIHRoZSBhY2FkZW1pYyB5ZWFyCgpgYGB7cn0KaW50ZXJ2YWxzIDwtIGxpc3QoCiAgaW50ZXJ2YWwxID0gYXMuUE9TSVhjdChjKCIyMDIyLTA4LTMxIiwgIjIwMjItMTItMTUiKSksCiAgaW50ZXJ2YWwyID0gYXMuUE9TSVhjdChjKCIyMDIzLTAxLTA0IiwgIjIwMjMtMDQtMDEiKSksCiAgaW50ZXJ2YWwzID0gYXMuUE9TSVhjdChjKCIyMDIzLTA0LTE0IiwgIjIwMjMtMDYtMTUiKSkKKQpgYGAKCiMgRnVuY3Rpb24gdG8gY2hlY2sgaWYgYSBkYXRlIGZhbGxzIHdpdGhpbiBhIGdpdmVuIGludGVydmFsIG9mIHRpbWUgYW5kIGFwcGx5IGFwcHJvcHJpYXRlIEJvb2xlYW4KCmBgYHtyfQpjaGVja19pbnRlcnZhbCA8LSBmdW5jdGlvbihkYXRlKSB7CiAgZm9yIChpIGluIDE6bGVuZ3RoKGludGVydmFscykpIHsKICAgIGludGVydmFsX3N0YXJ0IDwtIGludGVydmFsc1tbaV1dWzFdCiAgICBpbnRlcnZhbF9lbmQgPC0gaW50ZXJ2YWxzW1tpXV1bMl0KICAgIGlmIChkYXRlID49IGludGVydmFsX3N0YXJ0ICYgZGF0ZSA8PSBpbnRlcnZhbF9lbmQpIHsKICAgICAgcmV0dXJuKFRSVUUpCiAgICB9CiAgfQogIHJldHVybihGQUxTRSkKfQpgYGAKCmBgYHtyfQpkYXRhX3R3ZWV0cyRhY2FkZW1pY195ZWFyIDwtIHNhcHBseShkYXRhX3R3ZWV0cyRjcmVhdGVkX2F0LCBjaGVja19pbnRlcnZhbCkKcHJpbnQoZGF0YS5mcmFtZShpZCA9IGRhdGFfdHdlZXRzJGlkLCBhY2FkZW1pY195ZWFyID0gZGF0YV90d2VldHMkYWNhZGVtaWNfeWVhcikpCmBgYAoKIyBQbG90IGZvciB0aGUgbnVtYmVyIG9mIHR3ZWV0cyBwZXIgdGltZWZyYW1lIG9mIGVpdGhlciB2YWNhdGlvbiBvciBhY2FkZW1pYyB0aW1lCgpgYGB7cn0KYmFycGxvdCh0YWJsZShkYXRhX3R3ZWV0cyRhY2FkZW1pY195ZWFyKSwKICAgICAgICBtYWluID0gIk51bWJlciBvZiBUd2VldHMgcGVyIFRpbWVmcmFtZSIsCiAgICAgICAgeGxhYiA9ICJUaW1lIiwKICAgICAgICB5bGFiID0gIkNvdW50IiwKICAgICAgICB5bGltID0gYygwLCBtYXgodGFibGUoZGF0YV90d2VldHMkYWNhZGVtaWNfeWVhcikpICsgMTAwMCksCiAgICAgICAgbmFtZXMuYXJnID0gYygiVmFjYXRpb24iLCAiQWNhZGVtaWMiKSwKICAgICAgICBjb2wgPSBjKCIjOEU0NEFEIiwgIiNGMUM0MEYiKSkKCnRleHQoeCA9IGJhcnBsb3QoZGF0YV90d2VldHMkYWNhZGVtaWNfeWVhciwgcGxvdCA9IEZBTFNFKSwgCiAgICAgeSA9IHRhYmxlKGRhdGFfdHdlZXRzJGFjYWRlbWljX3llYXIpICsgMC41LCAKICAgICBsYWJlbHMgPSB0YWJsZShkYXRhX3R3ZWV0cyRhY2FkZW1pY195ZWFyKSwgCiAgICAgcG9zID0gMykKYGBgCgojIEZ1bmN0aW9uIHRvIGNvdW50IG51bWJlciBvZiB0d2VldHMgYW5kIGF2ZXJhZ2UgcGVyIGRheQoKYGBge3J9CmFuYWx5emVfdHdlZXRzIDwtIGZ1bmN0aW9uKGFjYWRlbWljX3llYXJfZmlsdGVyID0gVFJVRSkgewogICMgRmlsdGVyaW5nIHRoZSBkYXRhIGJhc2VkIG9uIHRoZSBhY2FkZW1pY195ZWFyX2ZpbHRlcgogIGZpbHRlcmVkX2RhdGEgPC0gZGF0YV90d2VldHMgJT4lCiAgICBmaWx0ZXIoYWNhZGVtaWNfeWVhciA9PSBhY2FkZW1pY195ZWFyX2ZpbHRlcikKICAKICAjIENvdW50IG9mIGRheXMgZm9yIGVhY2ggSEVJCiAgdW5pcXVlX2RheXMgPC0gZmlsdGVyZWRfZGF0YSAlPiUKICAgIGdyb3VwX2J5KGlkKSAlPiUKICAgIHN1bW1hcmlzZSh1bmlxdWVfZGF5cyA9IG5fZGlzdGluY3QoYXMuRGF0ZShjcmVhdGVkX2F0KSkpCiAgCiAgIyBDb3VudCBvZiB0d2VldHMgZm9yIGVhY2ggaWQKICBudW1iZXJfdHdlZXRzX2Jvb2xlYW4gPC0gZmlsdGVyZWRfZGF0YSAlPiUKICAgIGdyb3VwX2J5KGlkKSAlPiUKICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSkKICAKICAjIE5hbWluZyB0aGUgY29sdW1uIHJlc3BlY3RpbmcgdGhlIHRpbWUgcGVyaW9kCiAgeWVhciA8LSBpZmVsc2UoYWNhZGVtaWNfeWVhcl9maWx0ZXIsICJhY2FkZW1pY190aW1lIiwgInZhY2F0aW9uX3RpbWUiKQogIGNvbHVtbl9uYW1lIDwtIHBhc3RlMCgiYXZnX3R3ZWV0c19pbl8iLCB5ZWFyKQogIAogICMgQ29tYmluYXRpb24gb2YgZGF0YSBhbmQgY2FsY3VsYXRpb24gb2YgYXZlcmFnZSBwb3N0cyBwZXIgZGF5CiAgY29tYmluZWRfZGF0YSA8LSBsZWZ0X2pvaW4odW5pcXVlX2RheXMsIG51bWJlcl90d2VldHNfYm9vbGVhbiwgYnkgPSAiaWQiKQogIGNvbWJpbmVkX2RhdGEgPC0gY29tYmluZWRfZGF0YSAlPiUKICAgIG11dGF0ZSghIWNvbHVtbl9uYW1lIDo9IGNvdW50IC8gdW5pcXVlX2RheXMpCiAgCiAgcHJpbnQoY29tYmluZWRfZGF0YSkKICByZXR1cm4oY29tYmluZWRfZGF0YSkKfQpgYGAKCmBgYHtyfQpkYXRhX3R3ZWV0c19hY2FkZW1pYyA8LSBhbmFseXplX3R3ZWV0cygpCmRhdGFfdHdlZXRzX3ZhY2F0aW9ucyA8LSBhbmFseXplX3R3ZWV0cyhhY2FkZW1pY195ZWFyX2ZpbHRlciA9IEZBTFNFKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHR3ZWV0cyBkdXJpbmcgYWNhZGVtaWMgdGltZSBmb3IgZWFjaCBIRUkKCmBgYHtyfQpiYXJwbG90KGRhdGFfdHdlZXRzX2FjYWRlbWljJGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSwKICAgICAgICBuYW1lcy5hcmcgPSBkYXRhX3R3ZWV0c19hY2FkZW1pYyRpZCwKICAgICAgICBtYWluID0gIkF2ZXJhZ2UgVHdlZXRzIGR1cmluZyBBY2FkZW1pYyBUaW1lIiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJBdmVyYWdlIE51bWJlciBvZiBUd2VldHMiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heChkYXRhX3R3ZWV0c19hY2FkZW1pYyRhdmdfdHdlZXRzX2luX2FjYWRlbWljX3RpbWUpICsgNSksCiAgICAgICAgbGFzID0gMiwKICAgICAgICBjb2wgPSAiIzM0NDk1RSIpCgp0ZXh0KHggPSBiYXJwbG90KGRhdGFfdHdlZXRzX2FjYWRlbWljJGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSwgcGxvdCA9IEZBTFNFKSwKICAgICB5ID0gZGF0YV90d2VldHNfYWNhZGVtaWMkYXZnX3R3ZWV0c19pbl9hY2FkZW1pY190aW1lLAogICAgIGxhYmVscyA9IHJvdW5kKGRhdGFfdHdlZXRzX2FjYWRlbWljJGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSwgMiksCiAgICAgcG9zID0gMykKYGBgCgojIFBsb3QgZm9yIHRoZSBhdmVyYWdlIG51bWJlciBvZiB0d2VldHMgZHVyaW5nIHZhY2F0aW9uIHRpbWUgZm9yIGVhY2ggSEVJCgpgYGB7cn0KYmFycGxvdChkYXRhX3R3ZWV0c192YWNhdGlvbnMkYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lLAogICAgICAgIG5hbWVzLmFyZyA9IGRhdGFfdHdlZXRzX3ZhY2F0aW9ucyRpZCwKICAgICAgICBtYWluID0gIkF2ZXJhZ2UgVHdlZXRzIGR1cmluZyBWYWNhdGlvbiBUaW1lIiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJBdmVyYWdlIE51bWJlciBvZiBUd2VldHMiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heChkYXRhX3R3ZWV0c192YWNhdGlvbnMkYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lKSArIDUpLAogICAgICAgIGxhcyA9IDIsCiAgICAgICAgY29sID0gIiNEMzU0MDAiKQoKdGV4dCh4ID0gYmFycGxvdChkYXRhX3R3ZWV0c192YWNhdGlvbnMkYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lLCBwbG90ID0gRkFMU0UpLAogICAgIHkgPSBkYXRhX3R3ZWV0c192YWNhdGlvbnMkYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lLAogICAgIGxhYmVscyA9IHJvdW5kKGRhdGFfdHdlZXRzX3ZhY2F0aW9ucyRhdmdfdHdlZXRzX2luX3ZhY2F0aW9uX3RpbWUsIDIpLAogICAgIHBvcyA9IDMpCmBgYAoKIyBEYXRhIHByZXBhcmF0aW9uIGZvciBkYXkgb2YgdGhlIHdlZWsgCgpgYGB7cn0KIyBDcmVhdGluZyBuZXcgdGFibGUgdGhhdCBjb250YWlucyBhIG5ldyBjb2x1bW4gZm9yIHRoZSBkYXkgb2YgdGhlIHdlZWsKZGF0YV90d2VldHNfZGF5cyA8LSBkYXRhX3R3ZWV0cyAlPiUKICBtdXRhdGUoZGF5X29mX3dlZWsgPSB3ZWVrZGF5cyhjcmVhdGVkX2F0KSkKCiMgU2VsZWN0aW5nIG9ubHkgdGhlIGlkLCBjcmVhdGVkX2F0LCBhbmQgZGF5X29mX3dlZWsgY29sdW1ucyBmb3IgdGhlIG5ldyB0YWJsZQpkYXRhX3R3ZWV0c19kYXlzIDwtIGRhdGFfdHdlZXRzX2RheXMgJT4lCiAgc2VsZWN0KGlkLCBjcmVhdGVkX2F0LCBkYXlfb2Zfd2VlaykKCnByaW50KGRhdGFfdHdlZXRzX2RheXMpCmBgYAoKYGBge3J9CiMgR3JvdXBpbmcgYnkgaWQgYW5kIGRheV9vZl93ZWVrLCB0aGVuIGNvdW50aW5nIHRoZSBudW1iZXIgb2YgdHdlZXRzCm51bWJlcl90d2VldHNfZGF5cyA8LSBkYXRhX3R3ZWV0c19kYXlzICU+JQogIGdyb3VwX2J5KGlkLCBkYXlfb2Zfd2VlaykgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQoKIyBHcm91cGluZyBieSBpZCwgZGF5X29mX3dlZWsgYW5kIGRheSBjcmVhdGVkIGF0LCB0aGVuIGNvdW50aW5nIHRoZSBudW1iZXIgb2YgdHdlZXRzCm51bWJlcl90d2VldHNfcGVyX2RheSA8LSBkYXRhX3R3ZWV0c19kYXlzICU+JQogIG11dGF0ZShjcmVhdGVkX2RhdGUgPSBhcy5EYXRlKGNyZWF0ZWRfYXQpKSAlPiUKICBncm91cF9ieShpZCwgZGF5X29mX3dlZWssIGNyZWF0ZWRfZGF0ZSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQoKIyBGaW5kaW5nIGZvciBlYWNoIEhFSSB0aGUgYXZlcmFnZSBjb3VudCBvZiB0d2VldHMgcGVyIGRheQphdmVyYWdlX251bWJlcl90d2VldHNfcGVyX2RheSA8LSBudW1iZXJfdHdlZXRzX3Blcl9kYXkgJT4lCiAgZ3JvdXBfYnkoaWQsIGRheV9vZl93ZWVrKSAlPiUKICBzdW1tYXJpc2UoYXZlcmFnZV9jb3VudCA9IG1lYW4oY291bnQpKQoKcHJpbnQobnVtYmVyX3R3ZWV0c19kYXlzKQpgYGAKCiMgSGlnaGVzdCBhbmQgbG93ZXN0IHR3ZWV0cwoKYGBge3J9CiMgRmluZGluZyB0aGUgSEVJIHdpdGggdGhlIGxvd2VzdCBjb3VudCBvZiB0d2VldHMgcGVyIGRheQpsb3dlc3RfY291bnQgPC0gbnVtYmVyX3R3ZWV0c19kYXlzICU+JQogIGdyb3VwX2J5KGRheV9vZl93ZWVrKSAlPiUKICBzbGljZV9taW4ob3JkZXJfYnkgPSBjb3VudCkgJT4lCiAgc2VsZWN0KGRheV9vZl93ZWVrLCBpZCwgY291bnQpCgojIFNhbWUgYnV0IGhpZ2hlc3QgY291bnQgb2YgdHdlZXRzIHBlciBkYXkKaGlnaGVzdF9jb3VudCA8LSBudW1iZXJfdHdlZXRzX2RheXMgJT4lCiAgZ3JvdXBfYnkoZGF5X29mX3dlZWspICU+JQogIHNsaWNlX21heChvcmRlcl9ieSA9IGNvdW50KSAlPiUKICBzZWxlY3QoZGF5X29mX3dlZWssIGlkLCBjb3VudCkKCiMgQ29tYmluZSB0aGUgcmVzdWx0cwpoaWdoX2xvd19IRUkgPC0gYmluZF9yb3dzKGxvd2VzdF9jb3VudCwgaGlnaGVzdF9jb3VudCkgJT4lCiAgYXJyYW5nZShkYXlfb2Zfd2VlaykKCnByaW50KGhpZ2hfbG93X0hFSSkKYGBgCgojIFBsb3QgZm9yIHRoZSBsb3dlc3QgYW5kIGhpZ2hlc3QgY291bnQgb2YgdHdlZXRzIHBlciBkYXkgZm9yIGVhY2ggZGF5IG9mIHRoZSB3ZWVrCgpgYGB7cn0KZ2dwbG90KGhpZ2hfbG93X0hFSSwgYWVzKHggPSBkYXlfb2Zfd2VlaywgeSA9IGNvdW50LCBmaWxsID0gaWQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBjb3VudCksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLAogICAgICAgICAgICB2anVzdCA9IC0wLjUsCiAgICAgICAgICAgIHNpemUgPSAzKSArCiAgbGFicyh0aXRsZSA9ICJMb3dlc3QgYW5kIEhpZ2hlc3QgQ291bnQgb2YgVHdlZXRzIHBlciBEYXkgZm9yIEVhY2ggRGF5IG9mIHRoZSBXZWVrIiwKICAgICAgIHggPSAiRGF5IG9mIHRoZSBXZWVrIiwgeSA9ICJDb3VudCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSByYWluYm93KGxlbmd0aCh1bmlxdWUoaGlnaF9sb3dfSEVJJGlkKSkpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyBBdmVyYWdlIG9mIHR3ZWV0cwoKYGBge3J9CiMgRmluZGluZyB0aGUgSEVJIHdpdGggbG93ZXN0IGFuZCBoaWdoZXN0IGF2ZXJhZ2VkIGNvdW50IG9mIHR3ZWV0cyBwZXIgZGF5CmhpZ2hfbG93X2F2ZXJhZ2VfSEVJcyA8LSBhdmVyYWdlX251bWJlcl90d2VldHNfcGVyX2RheSAlPiUKICBncm91cF9ieShkYXlfb2Zfd2VlaykgJT4lCiAgZmlsdGVyKGF2ZXJhZ2VfY291bnQgPT0gbWF4KGF2ZXJhZ2VfY291bnQpIHwgYXZlcmFnZV9jb3VudCA9PSBtaW4oYXZlcmFnZV9jb3VudCkpICU+JQogIGFycmFuZ2UoZGF5X29mX3dlZWssIGlmZWxzZShhdmVyYWdlX2NvdW50ID09IG1pbihhdmVyYWdlX2NvdW50KSwgYXZlcmFnZV9jb3VudCwgLWF2ZXJhZ2VfY291bnQpKQoKcHJpbnQoaGlnaF9sb3dfYXZlcmFnZV9IRUlzKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGhpZ2hlc3QgYW5kIGxvd2VzdCBhdmVyYWdlIGNvdW50IG9mIHR3ZWV0cyBwZXIgZGF5IGZvciBlYWNoIGRheSBvZiB0aGUgd2VlawoKYGBge3J9CmdncGxvdChoaWdoX2xvd19hdmVyYWdlX0hFSXMsIGFlcyh4ID0gZGF5X29mX3dlZWssIHkgPSBhdmVyYWdlX2NvdW50LCBmaWxsID0gaWQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChhdmVyYWdlX2NvdW50LCAyKSksCiAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjcpLAogICAgICAgICAgICB2anVzdCA9IC0wLjUsCiAgICAgICAgICAgIHNpemUgPSAzKSArCiAgbGFicyh0aXRsZSA9ICJIaWdoZXN0IGFuZCBMb3dlc3QgQXZlcmFnZSBDb3VudCBvZiBUd2VldHMgcGVyIERheSBmb3IgRWFjaCBEYXkgb2YgdGhlIFdlZWsiLAogICAgICAgeCA9ICJEYXkgb2YgdGhlIFdlZWsiLCB5ID0gIkF2ZXJhZ2UgQ291bnQiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcmFpbmJvdyhsZW5ndGgodW5pcXVlKGhpZ2hfbG93X0hFSSRpZCkpKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKIyBWaWV3cyBMaWtlcyBSZXR3ZWV0cyBhbmQgUmVwbGllcwoKYGBge3J9CiMgVGFibGUgY29udGFpbmluZyB2aWV3cywgbGlrZXMsIHJldHdlZXRzIGFuZCByZXBsaWVzIGZvciBlYWNoIG1lZGlhIHR5cGUgZm9yIGVhY2ggSEVJCnR5cGVzX29mX3R3ZWV0cyA8LSBkYXRhX3R3ZWV0cyAlPiUKICAgICAgICAgICAgICBncm91cF9ieShpZCwgbWVkaWFfdHlwZSkgJT4lCiAgICAgICAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpLAogICAgICAgICAgICAgICAgICAgICAgICB2aWV3cyA9IHN1bSh2aWV3X2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICBsaWtlcyA9IHN1bShmYXZvcml0ZV9jb3VudCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgcmV0d2VldHMgPSBzdW0ocmV0d2VldF9jb3VudCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGllcyA9IHN1bShyZXBseV9jb3VudCwgbmEucm0gPSBUUlVFKSkKICAgICAgICAgICAgICAgICAgICAgICAgCnByaW50KHR5cGVzX29mX3R3ZWV0cykgICAgICAgICAgICAgICAgICAgICAgICAKYGBgCgpgYGB7cn0KIyBHcm91cGluZyBieSBIRUkgYW5kIGNhbGN1bGF0aW5nIHRoZSB0b3RhbCB2YWx1ZXMgb2Ygdmlld3MsIGxpa2VzIGFuZCByZXBsaWVzIGFjcm9zcyBhbGwgbWVkaWEgdHlwZXMKdG90YWxfdHdlZXRzX3N0YXRzIDwtIHR5cGVzX29mX3R3ZWV0cyAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKHRvdGFsX3ZpZXdzID0gc3VtKHZpZXdzKSwKICAgICAgICAgICAgdG90YWxfbGlrZXMgPSBzdW0obGlrZXMpLAogICAgICAgICAgICB0b3RhbF9yZXBsaWVzID0gc3VtKHJlcGxpZXMpKQoKcHJpbnQodG90YWxfdHdlZXRzX3N0YXRzKQpgYGAKCiMgRnVuY3Rpb24gZm9yIHBpZWNoYXJ0IGNyZWF0aW9uIGZvciB2aWV3cywgbGlrZXMgYW5kIHJlcGxpZXMgCgpgYGB7cn0KcGllX21ha2VyIDwtIGZ1bmN0aW9uKHRhcmdldF9pZCA9ICJkdWtlLmNzdiIpewogICMgRmlsdGVyaW5nIGRhdGEgZm9yIHRoZSBzcGVjaWZpYyBIRUkKICBoZWlfZGF0YSA8LSB0eXBlc19vZl90d2VldHMgJT4lCiAgICBmaWx0ZXIoaWQgPT0gdGFyZ2V0X2lkKQogIAogICMgQ2FsY3VsYXRlIHRvdGFsIHZpZXdzIGZvciBlYWNoIG1lZGlhIHR5cGUgZm9yIHRoZSBzcGVjaWZpYyBJRAogIGhlaV9tZWRpYSA8LSBoZWlfZGF0YSAlPiUKICAgIGdyb3VwX2J5KG1lZGlhX3R5cGUpICU+JQogICAgc3VtbWFyaXNlKHRvdGFsX3ZpZXdzID0gc3VtKHZpZXdzKSwKICAgICAgICAgICAgICB0b3RhbF9saWtlcyA9IHN1bShsaWtlcyksCiAgICAgICAgICAgICAgdG90YWxfcmVwbGllcyA9IHN1bShyZXBsaWVzKSkKICAKICAjIENhbGN1bGF0aW5nIHRoZSBwZXJjZW50YWdlIG9mIHZpZXdzIGZvciBlYWNoIG1lZGlhIHR5cGUgZm9yIHRoZSBzcGVjaWZpYyBJRAogIGhlaV9tZWRpYSRwZXJjZW50YWdlX3ZpZXcgPC0gaGVpX21lZGlhJHRvdGFsX3ZpZXdzIC8gc3VtKGhlaV9tZWRpYSR0b3RhbF92aWV3cykgKiAxMDAKICBoZWlfbWVkaWEkcGVyY2VudGFnZV9saWtlIDwtIGhlaV9tZWRpYSR0b3RhbF9saWtlcyAvIHN1bShoZWlfbWVkaWEkdG90YWxfbGlrZXMpICogMTAwCiAgaGVpX21lZGlhJHBlcmNlbnRhZ2VfcmVwbHkgPC0gaGVpX21lZGlhJHRvdGFsX3JlcGxpZXMgLyBzdW0oaGVpX21lZGlhJHRvdGFsX3JlcGxpZXMpICogMTAwCiAgCiAgIyBDcmVhdGluZyB0aGUgcGllIGNoYXJ0IGZvciB2aWV3cwogIGhlaV9waWVfY2hhcnRfdmlld3MgPC0gZ2dwbG90KGhlaV9tZWRpYSwgYWVzKHggPSAiIiwgeSA9IHBlcmNlbnRhZ2VfdmlldywgZmlsbCA9IG1lZGlhX3R5cGUpKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArCiAgICBjb29yZF9wb2xhcigieSIsIHN0YXJ0ID0gMCkgKwogICAgdGhlbWVfdm9pZCgpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsKICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZShtZWRpYV90eXBlLCAiXG4iLCB0b3RhbF92aWV3cywgIigiLCByb3VuZChwZXJjZW50YWdlX3ZpZXcsIDEpLCAiJSkiKSksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBjb2xvciA9ICIjRkZGRkZGIikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygibm9fbWVkaWEiID0gIiMyMTk2RjMiLCAiYW5pbWF0ZWRfZ2lmIiA9ICIjRTY3RTIyIiwgInBob3RvIiA9ICIjOEU0NEFEIiwgInZpZGVvIiA9ICIjMTM4RDc1IikpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiVmlld3MgZm9yIGVhY2ggbWVkaWEgdHlwZSAtIiwgdGFyZ2V0X2lkKSkKICAKICAjIENyZWF0aW5nIHRoZSBwaWUgY2hhcnQgZm9yIGxpa2VzCiAgaGVpX3BpZV9jaGFydF9saWtlcyA8LSBnZ3Bsb3QoaGVpX21lZGlhLCBhZXMoeCA9ICIiLCB5ID0gcGVyY2VudGFnZV9saWtlLCBmaWxsID0gbWVkaWFfdHlwZSkpICsKICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCB3aWR0aCA9IDEpICsKICAgIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQgPSAwKSArCiAgICB0aGVtZV92b2lkKCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlKG1lZGlhX3R5cGUsICJcbiIsIHRvdGFsX2xpa2VzLCAiKCIsIHJvdW5kKHBlcmNlbnRhZ2VfbGlrZSwgMSksICIlKSIpKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIGNvbG9yID0gIiNGRkZGRkYiKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJub19tZWRpYSIgPSAiI0U5MUU2MyIsICJhbmltYXRlZF9naWYiID0gIiM0QTE0OEMiLCAicGhvdG8iID0gIiM5MENBRjkiLCAidmlkZW8iID0gIiMwMEJGQTUiKSkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJMaWtlcyBmb3IgZWFjaCBtZWRpYSB0eXBlIC0iLCB0YXJnZXRfaWQpKQogIAogICMgQ3JlYXRpbmcgdGhlIHBpZSBjaGFydCBmb3IgcmVwbGllcwogIGhlaV9waWVfY2hhcnRfcmVwbGllcyA8LSBnZ3Bsb3QoaGVpX21lZGlhLCBhZXMoeCA9ICIiLCB5ID0gcGVyY2VudGFnZV9yZXBseSwgZmlsbCA9IG1lZGlhX3R5cGUpKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArCiAgICBjb29yZF9wb2xhcigieSIsIHN0YXJ0ID0gMCkgKwogICAgdGhlbWVfdm9pZCgpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsKICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZShtZWRpYV90eXBlLCAiXG4iLCB0b3RhbF9yZXBsaWVzLCAiKCIsIHJvdW5kKHBlcmNlbnRhZ2VfcmVwbHksIDEpLCAiJSkiKSksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBjb2xvciA9ICIjRkZGRkZGIikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygibm9fbWVkaWEiID0gIiM2NjY2MDAiLCAiYW5pbWF0ZWRfZ2lmIiA9ICIjOTlDQ0NDIiwgInBob3RvIiA9ICIjOTk2NkNDIiwgInZpZGVvIiA9ICIjMzMwMDAwIikpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZSgiUmVwbGllcyBmb3IgZWFjaCBtZWRpYSB0eXBlIC0iLCB0YXJnZXRfaWQpKQogIAogICMgUHJpbnQgdGhlIHBpZSBjaGFydHMKICBwcmludChoZWlfcGllX2NoYXJ0X3ZpZXdzKQogIHByaW50KGhlaV9waWVfY2hhcnRfbGlrZXMpCiAgcHJpbnQoaGVpX3BpZV9jaGFydF9yZXBsaWVzKQp9CmBgYAoKIyBQbG90IG9mIHBpZWNoYXJ0cyBmb3IgZWFjaCBIRUkKCmBgYHtyfQpwaWVfbWFrZXIoKQpwaWVfbWFrZXIoImVwZmwuY3N2IikKcGllX21ha2VyKCJnb2UuY3N2IikKcGllX21ha2VyKCJoYXJ2YXJkLmNzdiIpCnBpZV9tYWtlcigibGVpY2VzdGVyLmNzdiIpCnBpZV9tYWtlcigibWFuY2hlc3Rlci5jc3YiKQpwaWVfbWFrZXIoIm1pdC5jc3YiKQpwaWVfbWFrZXIoInNiLmNzdiIpCnBpZV9tYWtlcigic3RhbmZvcmQuY3N2IikKcGllX21ha2VyKCJ0cmluaXR5LmNzdiIpCnBpZV9tYWtlcigid3YuY3N2IikKcGllX21ha2VyKCJ5YWxlLmNzdiIpCmBgYAoKYGBge3J9CiMgQ2FsY3VsYXRpb24gb2YgbGlrZV9yYXRpbyBhbmQgcmVwbGllc19yYXRpbyBwZXJjZW50YWdlcwpyYXRpb3NfdHdlZXRzX3RhYmxlIDwtIHRvdGFsX3R3ZWV0c19zdGF0cyAlPiUKICBtdXRhdGUobGlrZV9yYXRpbyA9IHRvdGFsX2xpa2VzIC8gdG90YWxfdmlld3MgKiAxMDAsCiAgICAgICAgIHJlcGxpZXNfcmF0aW8gPSB0b3RhbF9yZXBsaWVzIC8gdG90YWxfdmlld3MgKiAxMDApCgojIENyZWF0aW9uIG9mIG5ldyB0YWJsZSB3aXRoIGVhY2ggSEVJLCBsaWtlX3JhdGlvLCBhbmQgcmVwbGllc19yYXRpbyAKaGVpX3R3ZWV0c19yYXRpb3MgPC0gcmF0aW9zX3R3ZWV0c190YWJsZSAlPiUKICBzZWxlY3QoaWQsIGxpa2VfcmF0aW8sIHJlcGxpZXNfcmF0aW8pICU+JQogIGRpc3RpbmN0KCkKCnByaW50KGhlaV90d2VldHNfcmF0aW9zKQpgYGAKCiMgUGxvdCBmb3IgbGlrZV9yYXRpbyBhbmQgcmVwbGllc19yYXRpbyBmb3IgZWFjaCBIRUkKCmBgYHtyfQpnZ3Bsb3QoaGVpX3R3ZWV0c19yYXRpb3MsIGFlcyh4ID0gaWQpKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSBsaWtlX3JhdGlvLCBmaWxsID0gIkxpa2UgUmF0aW8iKSwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fYmFyKGFlcyh5ID0gcmVwbGllc19yYXRpbywgZmlsbCA9ICJSZXBseXMgUmF0aW8iKSwgc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fdGV4dChhZXMoeSA9IGxpa2VfcmF0aW8sIGxhYmVsID0gcm91bmQobGlrZV9yYXRpbywgMikpLCB2anVzdCA9IC0wLjUsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCBzaXplID0gMywgY29sb3IgPSAiIzAwMDAwMCIpICsKICBnZW9tX3RleHQoYWVzKHkgPSByZXBsaWVzX3JhdGlvLCBsYWJlbCA9IHJvdW5kKHJlcGxpZXNfcmF0aW8sIDIpKSwgdmp1c3QgPSAtMC41LCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgc2l6ZSA9IDMsIGNvbG9yID0gIiNGRkZGRkYiKSArCiAgbGFicyh0aXRsZSA9ICJMaWtlIGFuZCBSZXBseXMgUmF0aW9zIGJ5IEhFSSIsCiAgICAgICB4ID0gIkhFSSIsCiAgICAgICB5ID0gIlJhdGlvICglKSIsCiAgICAgICBmaWxsID0gIk1ldHJpYyIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJMaWtlIFJhdGlvIiA9ICIjMjE5NkYzIiwgIlJlcGx5cyBSYXRpbyIgPSAiI0Y0NDMzNiIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkKYGBgCgpgYGB7cn0KIyBUYWJsZSB3aXRoIGF2ZXJhZ2VzIG9mIHZpZXdzLCBsaWtlcywgcmV0d2VldHMgYW5kIHJlcGxpZXMKdHlwZXNfb2ZfdHdlZXRzX3Blcl90d2VldCA8LSB0eXBlc19vZl90d2VldHMgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KGlkLCBtZWRpYV90eXBlKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKGF2Z192aWV3cyA9IG1lYW4odmlld3MgLyBjb3VudCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhdmdfbGlrZXMgPSBtZWFuKGxpa2VzIC8gY291bnQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXZnX3JldHdlZXRzID0gbWVhbihyZXR3ZWV0cyAvIGNvdW50KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF2Z19yZXBsaWVzID0gbWVhbihyZXBsaWVzIC8gY291bnQpKQoKcHJpbnQodHlwZXNfb2ZfdHdlZXRzX3Blcl90d2VldCkKYGBgCgpgYGB7cn0KIyBHcm91cGluZyBieSBIRUkgYW5kIGNhbGN1bGF0aW5nIHRoZSBhdmVyYWdlIHZhbHVlcyBvZiB2aWV3cywgbGlrZXMgYW5kIHJlcGxpZXMgYWNyb3NzIGFsbCBtZWRpYSB0eXBlcwp0b3RhbF9hdmVyYWdlX3N0YXRzIDwtIHR5cGVzX29mX3R3ZWV0c19wZXJfdHdlZXQgJT4lCiAgZ3JvdXBfYnkoaWQpICU+JQogIHN1bW1hcmlzZShhdmdfdmlld3MgPSBzdW0oYXZnX3ZpZXdzKSwKICAgICAgICAgICAgYXZnX2xpa2VzID0gc3VtKGF2Z19saWtlcyksCiAgICAgICAgICAgIGF2Z19yZXBsaWVzID0gc3VtKGF2Z19yZXBsaWVzKSkKCnByaW50KHRvdGFsX2F2ZXJhZ2Vfc3RhdHMpCmBgYAoKYGBge3J9CiMgQ2FsY3VsYXRpb24gb2YgbGlrZV9yYXRpbyBhbmQgcmVwbGllc19yYXRpbyBwZXJjZW50YWdlcwpyYXRpb3NfYXZlcmFnZV90YWJsZSA8LSB0b3RhbF9hdmVyYWdlX3N0YXRzICU+JQogIG11dGF0ZShsaWtlX3JhdGlvID0gYXZnX2xpa2VzIC8gYXZnX3ZpZXdzICogMTAwLAogICAgICAgICByZXBsaWVzX3JhdGlvID0gYXZnX3JlcGxpZXMgLyBhdmdfdmlld3MgKiAxMDApCgojIENyZWF0aW9uIG9mIG5ldyB0YWJsZSB3aXRoIGVhY2ggSEVJLCBsaWtlX3JhdGlvLCBhbmQgcmVwbGllc19yYXRpbyAKaGVpX2F2ZXJhZ2VfcmF0aW9zIDwtIHJhdGlvc19hdmVyYWdlX3RhYmxlICU+JQogIHNlbGVjdChpZCwgbGlrZV9yYXRpbywgcmVwbGllc19yYXRpbykgJT4lCiAgZGlzdGluY3QoKQoKcHJpbnQoaGVpX2F2ZXJhZ2VfcmF0aW9zKQpgYGAKCiMgUGxvdCBmb3IgbGlrZV9yYXRpbyBhbmQgcmVwbGllc19yYXRpbyBmb3IgZWFjaCBIRUkKCmBgYHtyfQpnZ3Bsb3QoaGVpX2F2ZXJhZ2VfcmF0aW9zLCBhZXMoeCA9IGlkKSkgKwogIGdlb21fYmFyKGFlcyh5ID0gbGlrZV9yYXRpbywgZmlsbCA9ICJMaWtlIFJhdGlvIiksIHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBnZW9tX2JhcihhZXMoeSA9IHJlcGxpZXNfcmF0aW8sIGZpbGwgPSAiUmVwbGllcyBSYXRpbyIpLCBzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgZ2VvbV90ZXh0KGFlcyh5ID0gbGlrZV9yYXRpbywgbGFiZWwgPSByb3VuZChsaWtlX3JhdGlvLCAyKSksIHZqdXN0ID0gLTAuNSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHNpemUgPSAzLCBjb2xvciA9ICIjMDAwMDAwIikgKwogIGdlb21fdGV4dChhZXMoeSA9IHJlcGxpZXNfcmF0aW8sIGxhYmVsID0gcm91bmQocmVwbGllc19yYXRpbywgMikpLCB2anVzdCA9IC0wLjUsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjkpLCBzaXplID0gMywgY29sb3IgPSAiI0ZGRkZGRiIpICsKICBsYWJzKHRpdGxlID0gIkxpa2UgYW5kIFJlcGxpZXMgUmF0aW9zIGJ5IEhFSSIsCiAgICAgICB4ID0gIkhFSSIsCiAgICAgICB5ID0gIlJhdGlvICglKSIsCiAgICAgICBmaWxsID0gIk1ldHJpYyIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJMaWtlIFJhdGlvIiA9ICIjMzMwMDY2IiwgIlJlcGxpZXMgUmF0aW8iID0gIiNGRjY2NjYiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSkpCmBgYAoKIyBIYXNodGFncwoKYGBge3J9CiMgVGFibGUgd2l0aCBudW1iZXIgb2YgdW5pcXVlIGhhc2h0YWdzCnVuaXF1ZV9oYXNodGFncyA8LSBkYXRhX3R3ZWV0cyAlPiUKICAgICAgICAgICAgICAgIGdyb3VwX2J5KGlkKSAlPiUKICAgICAgICAgICAgICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSwKICAgICAgICAgICAgICAgICAgICAgICAgICB1bmlxdWVfaGFzaHRhZ3MgPSBsZW5ndGgodW5pcXVlKGhhc2h0YWdzKSkpCgpwcmludCh1bmlxdWVfaGFzaHRhZ3MpCmBgYAoKIyBQbG90IGZvciB0aGUgY291bnQgb2YgdW5pcXVlIGhhc2h0YWdzIGZvciBlYWNoIEhFSQoKYGBge3J9CmJhcnBsb3QodW5pcXVlX2hhc2h0YWdzJHVuaXF1ZV9oYXNodGFncywKICAgICAgICBuYW1lcy5hcmcgPSB1bmlxdWVfaGFzaHRhZ3MkaWQsCiAgICAgICAgbWFpbiA9ICJVbmlxdWUgSGFzaHRhZ3MgZm9yIEVhY2ggSEVJIiwKICAgICAgICB4bGFiID0gIkhFSSIsCiAgICAgICAgeWxhYiA9ICJDb3VudCBvZiBVbmlxdWUgSGFzaHRhZ3MiLAogICAgICAgIHlsaW0gPSBjKDAsIG1heCh1bmlxdWVfaGFzaHRhZ3MkdW5pcXVlX2hhc2h0YWdzKSArIDUwKSwKICAgICAgICBsYXMgPSAyLAogICAgICAgIGNvbD0gIiMxNkEwODUiKQoKdGV4dCh4ID0gYmFycGxvdCh1bmlxdWVfaGFzaHRhZ3MkdW5pcXVlX2hhc2h0YWdzLCBwbG90ID0gRkFMU0UpLAogICAgIHkgPSB1bmlxdWVfaGFzaHRhZ3MkdW5pcXVlX2hhc2h0YWdzLAogICAgIGxhYmVscyA9IHJvdW5kKHVuaXF1ZV9oYXNodGFncyR1bmlxdWVfaGFzaHRhZ3MsIDIpLAogICAgIHBvcyA9IDMpCmBgYAoKIyBIZWF0bWFwcwoKYGBge3J9CiMgQ3JlYXRlIGNvbHVtbiBob3VyIGZyb20gY3JlYXRlZF9hdApkYXRhX3R3ZWV0c19kYXlzJGNyZWF0ZWRfaG91ciA8LSBhcy5udW1lcmljKGZvcm1hdChkYXRhX3R3ZWV0c19kYXlzJGNyZWF0ZWRfYXQsICIlSCIpKQpgYGAKCiMgRnVuY3Rpb24gdG8gcGxvdCBoZWF0bWFwIGZvciB2YXJpb3VzIEhFSXMKCmBgYHtyfQpoZWF0bWFwX21ha2VyIDwtIGZ1bmN0aW9uKHRhcmdldF9pZCA9ICJkdWtlLmNzdiIpewogICMgRmlsdGVyaW5nIGRhdGEgZm9yIHRoZSBzcGVjaWZpYyBIRUkKICB0YXJnZXRfZGF0YSA8LSBkYXRhX3R3ZWV0c19kYXlzICU+JQogICAgZmlsdGVyKGlkID09IHRhcmdldF9pZCkKICAKICAjIEdyb3VwaW5nIGJ5IGRheSBvZiB0aGUgd2VlayBhbmQgaG91ciwgYW5kIGNvdW50aW5nIHRoZSBudW1iZXIgb2YgdHdlZXRzCiAgdHdlZXRfY291bnRzIDwtIHRhcmdldF9kYXRhICU+JQogICAgZ3JvdXBfYnkoZGF5X29mX3dlZWssIGNyZWF0ZWRfaG91cikgJT4lCiAgICBzdW1tYXJpc2UobnVtX3R3ZWV0cyA9IG4oKSkKICAKICAjIFBsb3R0aW5nIGhlYXRtYXAKICBnZ3Bsb3QodHdlZXRfY291bnRzLCBhZXMoeCA9IGRheV9vZl93ZWVrLCB5ID0gY3JlYXRlZF9ob3VyLCBmaWxsID0gbnVtX3R3ZWV0cykpICsKICAgIGdlb21fdGlsZSgpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICJibHVlIikgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJUd2VldCBIZWF0bWFwIGZvciIsIHRhcmdldF9pZCksCiAgICAgICAgIHggPSAiRGF5IG9mIHRoZSB3ZWVrIiwKICAgICAgICAgeSA9ICJIb3VyIG9mIHRoZSBkYXkiKQp9CgpoZWF0bWFwX21ha2VyKCkKaGVhdG1hcF9tYWtlcigiZXBmbC5jc3YiKQpoZWF0bWFwX21ha2VyKCJnb2UuY3N2IikKaGVhdG1hcF9tYWtlcigiaGFydmFyZC5jc3YiKQpoZWF0bWFwX21ha2VyKCJsZWljZXN0ZXIuY3N2IikKaGVhdG1hcF9tYWtlcigibWFuY2hlc3Rlci5jc3YiKQpoZWF0bWFwX21ha2VyKCJtaXQuY3N2IikKaGVhdG1hcF9tYWtlcigic2IuY3N2IikKaGVhdG1hcF9tYWtlcigic3RhbmZvcmQuY3N2IikKaGVhdG1hcF9tYWtlcigidHJpbml0eS5jc3YiKQpoZWF0bWFwX21ha2VyKCJ3di5jc3YiKQpoZWF0bWFwX21ha2VyKCJ5YWxlLmNzdiIpCmBgYAoKIyBUZXh0CgpgYGB7cn0KZGF0YV90d2VldHNfY29udGVudCA8LSBkYXRhX3R3ZWV0cyAlPiUKICAgICAgICAgICAgc2VsZWN0KGlkLCB0ZXh0KQoKIyBDb3VudGluZyBudW1iZXIgb2Ygd29yZHMKZGF0YV90d2VldHNfY29udGVudCA8LSBkYXRhX3R3ZWV0c19jb250ZW50ICU+JQogIG11dGF0ZShudW1fd29yZHMgPSBsZW5ndGhzKHN0cnNwbGl0KHRleHQsICJcXHMrIikpKQoKcHJpbnQoZGF0YV90d2VldHNfY29udGVudCkKCiMgR3JvdXBpbmcgYnkgSEVJIGFuZCBjYWxjdWxhdGUgYXZlcmFnZSwgbWluaW11bSwgYW5kIG1heGltdW0gdmFsdWVzIG9mIG51bWJlciBvZiB3b3JkcwpkYXRhX3R3ZWV0c19jb250ZW50X21ldHJpY3MgPC0gZGF0YV90d2VldHNfY29udGVudCAlPiUKICBncm91cF9ieShpZCkgJT4lCiAgc3VtbWFyaXNlKGF2ZXJhZ2VfbnVtX3dvcmRzID0gbWVhbihudW1fd29yZHMpLAogICAgICAgICAgICBtaW5fbnVtX3dvcmRzID0gbWluKG51bV93b3JkcyksCiAgICAgICAgICAgIG1heF9udW1fd29yZHMgPSBtYXgobnVtX3dvcmRzKSkKcHJpbnQoZGF0YV90d2VldHNfY29udGVudF9tZXRyaWNzKQpgYGAKCiMgUGxvdCBmb3IgdGhlIGF2ZXJhZ2UsIG1heGltdW0gYW5kIG1pbmltdW0gdmFsdWVzIG9mIHdvcmRzIGZvciBlYWNoIEhFSQoKYGBge3J9CmdncGxvdChkYXRhX3R3ZWV0c19jb250ZW50X21ldHJpY3MsIGFlcyh4ID0gaWQsIHkgPSBhdmVyYWdlX251bV93b3JkcykpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9ICJBdmVyYWdlIikpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbWluX251bV93b3JkcywgeW1heCA9IG1heF9udW1fd29yZHMsIGNvbG9yID0gIlJhbmdlIiksIHdpZHRoID0gMC4yKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkF2ZXJhZ2UiID0gIiMxOTc2RDIiLCAiUmFuZ2UiID0gIiNFRjUzNTAiKSkgKwogIGxhYnModGl0bGUgPSAiV29yZCBDb3VudCBTdW1tYXJ5IGJ5IEhFSSIsCiAgICAgICB4ID0gIkhFSSIsCiAgICAgICB5ID0gIk51bWJlciBvZiBXb3JkcyIsCiAgICAgICBjb2xvciA9ICJNZXRyaWMiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkKYGBgCgojIE5vdyBjb21tZW50cyAKCmBgYHtyfQpkYXRhX3JlcGxpZXMgPC0gZGF0YVtkYXRhJHR5cGUgPT0gIlJlcGx5IiwgXQoKZGF0YV9yZXBsaWVzCmBgYAoKIyBJbnRlcmFjdGlvbnMgdG8gcmVwbGllcwoKYGBge3J9CiMgVGFibGUgY29udGFpbmluZyB2aWV3cywgbGlrZXMsIHJldHdlZXRzIGFuZCByZXBsaWVzIGZvciBlYWNoIG1lZGlhIHR5cGUgZm9yIGVhY2ggSEVJCnR5cGVzX29mX3JlcGxpZXMgPC0gZGF0YV9yZXBsaWVzICU+JQogICAgICAgICAgICAgIGdyb3VwX2J5KGlkLCBtZWRpYV90eXBlKSAlPiUKICAgICAgICAgICAgICBzdW1tYXJpc2UoY291bnQgPSBuKCksCiAgICAgICAgICAgICAgICAgICAgICAgIHZpZXdzID0gc3VtKHZpZXdfY291bnQsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxpa2VzID0gc3VtKGZhdm9yaXRlX2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICByZXR3ZWV0cyA9IHN1bShyZXR3ZWV0X2NvdW50LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICByZXBsaWVzID0gc3VtKHJlcGx5X2NvdW50LCBuYS5ybSA9IFRSVUUpKQogICAgICAgICAgICAgICAgICAgICAgICAKcHJpbnQodHlwZXNfb2ZfcmVwbGllcykgICAgICAgICAgICAgICAgICAgICAgICAKYGBgCgpgYGB7cn0KIyBHcm91cGluZyBieSBIRUkgYW5kIGNhbGN1bGF0aW5nIHRoZSB0b3RhbCB2YWx1ZXMgb2Ygdmlld3MsIGxpa2VzIGFuZCByZXBsaWVzIGFjcm9zcyBhbGwgbWVkaWEgdHlwZXMKdG90YWxfcmVwbGllc19zdGF0cyA8LSB0eXBlc19vZl9yZXBsaWVzICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBzdW1tYXJpc2UodG90YWxfdmlld3MgPSBzdW0odmlld3MpLAogICAgICAgICAgICB0b3RhbF9saWtlcyA9IHN1bShsaWtlcyksCiAgICAgICAgICAgIHRvdGFsX3JlcGxpZXMgPSBzdW0ocmVwbGllcykpCgpwcmludCh0b3RhbF9yZXBsaWVzX3N0YXRzKQpgYGAKCmBgYHtyfQojIENhbGN1bGF0aW9uIG9mIGxpa2VfcmF0aW8gYW5kIHJlcGxpZXNfcmF0aW8gcGVyY2VudGFnZXMKcmF0aW9zX3JlcGxpZXNfdGFibGUgPC0gdG90YWxfcmVwbGllc19zdGF0cyAlPiUKICBtdXRhdGUobGlrZV9yYXRpbyA9IHRvdGFsX2xpa2VzIC8gdG90YWxfdmlld3MgKiAxMDAsCiAgICAgICAgIHJlcGxpZXNfcmF0aW8gPSB0b3RhbF9yZXBsaWVzIC8gdG90YWxfdmlld3MgKiAxMDApCgojIENyZWF0aW9uIG9mIG5ldyB0YWJsZSB3aXRoIGVhY2ggSEVJLCBsaWtlX3JhdGlvLCBhbmQgcmVwbGllc19yYXRpbyAKaGVpX3JlcGxpZXNfcmF0aW9zIDwtIHJhdGlvc19yZXBsaWVzX3RhYmxlICU+JQogIHNlbGVjdChpZCwgbGlrZV9yYXRpbywgcmVwbGllc19yYXRpbykgJT4lCiAgZGlzdGluY3QoKQoKcHJpbnQoaGVpX3JlcGxpZXNfcmF0aW9zKQpgYGAKCiMgQ2x1c3RlcnMKCmBgYHtyfQojIENyZWF0aW5nIHRhYmxlIGZvciBjbHVzdGVyIGFsZ29yaXRobXMKCiMgSm9pbmluZyBhdHRyaWJ1dGUgY291bnQgKG51bWJlciBvZiB0d2VldHMpIGFuZCB1bmlxdWVfaGFzaHRhZ3MgKG51bWJlciBvZiB1bmlxdWUgaGFzaHRhZ3MpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShzZWxlY3QodW5pcXVlX2hhc2h0YWdzLCBpZCwgdW5pcXVlX2hhc2h0YWdzKSwgc2VsZWN0KG51bWJlcl90d2VldHMsIGlkLCBjb3VudCksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z190d2VldHNfcGVyX2RheXMgKGF2ZXJhZ2Ugb2YgdHdlZXRzIHBlciBkYXkpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QodHdlZXRzX3Blcl9kYXksIGlkLCBhdmdfdHdlZXRzX3Blcl9kYXlzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgYXZnX3R3ZWV0c19wZXJfd2Vla3MgKGF2ZXJhZ2Ugb2YgdHdlZXRzIHBlciB3ZWVrKSBwZXIgSEVJCmNsdXN0ZXJfdGFibGUgPC0gbWVyZ2UoY2x1c3Rlcl90YWJsZSwgc2VsZWN0KHR3ZWV0c19wZXJfd2VlaywgaWQsIGF2Z190d2VldHNfcGVyX3dlZWtzKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgYXZnX3R3ZWV0c19pbl9hY2FkZW1pY190aW1lIChhdmVyYWdlIG9mIHR3ZWV0cyBkdXJpbmcgYWNhZGVtaWMgdGltZSkgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChkYXRhX3R3ZWV0c19hY2FkZW1pYywgaWQsIGF2Z190d2VldHNfaW5fYWNhZGVtaWNfdGltZSksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z190d2VldHNfaW5fdmFjYXRpb25fdGltZSAoYXZlcmFnZSBvZiB0d2VldHMgZHVyaW5nIHZhY2F0aW9uIHRpbWUpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QoZGF0YV90d2VldHNfdmFjYXRpb25zLCBpZCwgYXZnX3R3ZWV0c19pbl92YWNhdGlvbl90aW1lKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKCiMgSm9pbmluZyBhdHRyaWJ1dGUgdG90YWxfdmlld3MgKHRvdGFsIG51bWJlciBvZiB2aWV3cyksIHRvdGFsX2xpa2VzICh0b3RhbCBudW1iZXIgb2YgbGlrZXMpIGFuZCB0b3RhbF9yZXBsaWVzICh0b3RhbCBudW1iZXIgb2YgcmVwbGllcykgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdCh0b3RhbF90d2VldHNfc3RhdHMsIGlkLCB0b3RhbF92aWV3cywgdG90YWxfbGlrZXMsIHRvdGFsX3JlcGxpZXMpLCBieSA9ICJpZCIsIGFsbD1UUlVFKQoKIyBKb2luaW5nIGF0dHJpYnV0ZSBsaWtlX3JhdGlvIChyYXRpbyBvZiB0b3RhbCBudW1iZXIgb2YgbGlrZXMpIGFuZCByZXBsaWVzX3JhdGlvIChyYXRpbyBvZiB0b3RhbCBudW1iZXIgb2YgcmVwbGllcykgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChoZWlfdHdlZXRzX3JhdGlvcywgaWQsIGxpa2VfcmF0aW8sIHJlcGxpZXNfcmF0aW8pLCBieSA9ICJpZCIsIGFsbD1UUlVFKQpjbHVzdGVyX3RhYmxlIDwtIGNsdXN0ZXJfdGFibGUgJT4lCiAgcmVuYW1lKHRvdGFsX2xpa2VfcmF0aW8gPSBsaWtlX3JhdGlvLCAKICAgICAgICAgdG90YWxfcmVwbGllc19yYXRpbyA9IHJlcGxpZXNfcmF0aW8pCgojIEpvaW5pbmcgYXR0cmlidXRlIGF2Z192aWV3cyAoYXZlcmFnZSBudW1iZXIgb2Ygdmlld3MpLCBhdmdfbGlrZXMgKGF2ZXJhZ2UgbnVtYmVyIG9mIGxpa2VzKSBhbmQgYXZnX3JlcGxpZXMgKGF2ZXJhZ2UgbnVtYmVyIG9mIHJlcGxpZXMpIHBlciBIRUkKY2x1c3Rlcl90YWJsZSA8LSBtZXJnZShjbHVzdGVyX3RhYmxlLCBzZWxlY3QodG90YWxfYXZlcmFnZV9zdGF0cywgaWQsIGF2Z192aWV3cywgYXZnX2xpa2VzLCBhdmdfcmVwbGllcyksIGJ5ID0gImlkIiwgYWxsPVRSVUUpCgojIEpvaW5pbmcgYXR0cmlidXRlIGxpa2VfcmF0aW8gKHJhdGlvIG9mIGF2ZXJhZ2UgbnVtYmVyIG9mIGxpa2VzKSBhbmQgcmVwbGllc19yYXRpbyAocmF0aW8gb2YgYXZlcmFnZSBudW1iZXIgb2YgcmVwbGllcykgcGVyIEhFSQpjbHVzdGVyX3RhYmxlIDwtIG1lcmdlKGNsdXN0ZXJfdGFibGUsIHNlbGVjdChoZWlfYXZlcmFnZV9yYXRpb3MsIGlkLCBsaWtlX3JhdGlvLCByZXBsaWVzX3JhdGlvKSwgYnkgPSAiaWQiLCBhbGw9VFJVRSkKY2x1c3Rlcl90YWJsZSA8LSBjbHVzdGVyX3RhYmxlICU+JQogIHJlbmFtZShhdmdfbGlrZV9yYXRpbyA9IGxpa2VfcmF0aW8sIAogICAgICAgICBhdmdfcmVwbGllc19yYXRpbyA9IHJlcGxpZXNfcmF0aW8pCgpwcmludChjbHVzdGVyX3RhYmxlKQpgYGAKCiMgRnVuY3Rpb24gZm9yIGNsdXN0ZXIgbWV0aG9kCgpgYGB7cn0KY2x1c3Rlcl9tYWtlciA8LSBmdW5jdGlvbihzZWVkID0gMTIzLCBudW1fY2x1c3RlcnMgPSAzLCB0YWJsZSl7CiAgc2V0LnNlZWQoMTIzKQogIAogICMgRXhjbHVkaW5nIGlkIGNvbHVtbiBmb3IgY2x1c3RlcmluZwogIGNsdXN0ZXJfZGF0YSA8LSBzZWxlY3QodGFibGUsIC1pZCkKICAKICAjIFNjYWxpbmcgdGhlIGRhdGEgZm9yIGttZWFucyBtZXRob2QKICBzY2FsZWRfZGF0YSA8LSBzY2FsZShjbHVzdGVyX2RhdGEpCiAgCiAga21lYW5zX3Jlc3VsdCA8LSBrbWVhbnMoc2NhbGVkX2RhdGEsIGNlbnRlcnMgPSBudW1fY2x1c3RlcnMpCiAgCiAgcHJpbnQoa21lYW5zX3Jlc3VsdCRjZW50ZXJzKQogIHByaW50KGttZWFuc19yZXN1bHQkY2x1c3RlcikKICAKICByZXR1cm4oa21lYW5zX3Jlc3VsdCkKfQpgYGAKCiMgRnVuY3Rpb24gdG8gYWRkIGlkcyB0byBiZXR0ZXIgdmlzdWFsaXplIHJlc3VsdHMKCmBgYHtyfQpjbHVzdGVyX2lkX21ha2VyIDwtIGZ1bmN0aW9uKGttZWFuc19yZXN1bHQsIHRhYmxlKXsKICAjIE1lcmdpbmcgdGhlIGNsdXN0ZXIgYXNzaWdubWVudHMgd2l0aCB0aGUgb3JpZ2luYWwgZGF0YQogIGNsdXN0ZXJfYXNzaWdubWVudHMgPC0gZGF0YS5mcmFtZShpZCA9IHRhYmxlJGlkLCBjbHVzdGVyID0ga21lYW5zX3Jlc3VsdCRjbHVzdGVyKQoKICBwcmludChjbHVzdGVyX2Fzc2lnbm1lbnRzKQogIHBsb3Qoa21lYW5zX3Jlc3VsdCRjbHVzdGVyKQp9CmBgYAoKIyBUaHJlZSBjbHVzdGVycyB3aXRoIHNlZWQgMTIzCgpgYGB7cn0KY2x1c3Rlcl8xMjNfMyA8LSBjbHVzdGVyX21ha2VyKHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKY2x1c3Rlcl9pZF9tYWtlcihjbHVzdGVyXzEyM18zLCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmBgYAoKIyBTaXggY2x1c3RlcnMgd2l0aCBzZWVkIDEyMwoKYGBge3J9CmNsdXN0ZXJfMTIzXzYgPC0gY2x1c3Rlcl9tYWtlcihudW1fY2x1c3RlcnMgPSA2LCB0YWJsZSA9IGNsdXN0ZXJfdGFibGUpCmNsdXN0ZXJfaWRfbWFrZXIoY2x1c3Rlcl8xMjNfNiwgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpgYGAKCiMgRm91ciBjbHVzdGVycyB3aXRoIHNlZWQgNDg1NQoKYGBge3J9CmNsdXN0ZXJfMTIzXzMgPC0gY2x1c3Rlcl9tYWtlcihzZWVkID0gNDg1NSwgbnVtX2NsdXN0ZXJzID0gNCwgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpjbHVzdGVyX2lkX21ha2VyKGNsdXN0ZXJfMTIzXzMsIHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKYGBgCgojIFNpeCBjbHVzdGVycyB3aXRoIHNlZWQgNDg1NQoKYGBge3J9CmNsdXN0ZXJfMTIzXzYgPC0gY2x1c3Rlcl9tYWtlcihzZWVkID0gNDg1NSwgbnVtX2NsdXN0ZXJzID0gNiwgdGFibGUgPSBjbHVzdGVyX3RhYmxlKQpjbHVzdGVyX2lkX21ha2VyKGNsdXN0ZXJfMTIzXzYsIHRhYmxlID0gY2x1c3Rlcl90YWJsZSkKYGBgCgo=